-
Notifications
You must be signed in to change notification settings - Fork 1
/
http-wordpress-plugins.nse
170 lines (134 loc) · 5.67 KB
/
http-wordpress-plugins.nse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
local coroutine = require "coroutine"
local http = require "http"
local io = require "io"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Tries to obtain a list of installed WordPress plugins by brute force
testing for known plugins.
The script will brute force the /wp-content/plugins/ folder with a dictionnary
of 14K (and counting) known WP plugins. Anything but a 404 means that a given
plugin directory probably exists, so the plugin probably also does.
The available plugins for Wordpress is huge and despite the efforts of Nmap to
parallelize the queries, a whole search could take an hour or so. That's why
the plugin list is sorted by popularity and by default the script will only
check the first 100 ones. Users can tweak this with an option (see below).
]]
---
-- @args http-wordpress-plugins.root If set, points to the blog root directory on the website. If not, the script will try to find a WP directory installation or fall back to root.
-- @args http-wordpress-plugins.search As the plugins list contains tens of thousand of plugins, this script will only search the 100 most popular ones by default.
-- Use this option with a number or "all" as an argument for a more comprehensive brute force.
--
-- @usage
-- nmap --script=http-wordpress-plugins --script-args http-wordpress-plugins.root="/blog/",http-wordpress-plugins.search=500 <targets>
--
--@output
-- Interesting ports on my.woot.blog (123.123.123.123):
-- PORT STATE SERVICE REASON
-- 80/tcp open http syn-ack
-- | http-wordpress-plugins:
-- | search amongst the 500 most popular plugins
-- | akismet
-- | wp-db-backup
-- | all-in-one-seo-pack
-- | stats
-- |_ wp-to-twitter
author = "Ange Gutek"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
local DEFAULT_PLUGINS_SEARCH = 100
portrule = shortport.service("http")
local function read_data_file(file)
return coroutine.wrap(function()
for line in file:lines() do
if not line:match("^%s*#") and not line:match("^%s*$") then
coroutine.yield(line)
end
end
end)
end
action = function(host, port)
local result = {}
local all = {}
local bfqueries = {}
--Check if the wp plugins list exists
local wp_plugins_file = nmap.fetchfile("nselib/data/wp-plugins.lst")
if not wp_plugins_file then
return false, "Couldn't find wp-plugins.lst (should be in nselib/data)"
end
local file = io.open(wp_plugins_file, "r")
if not file then
return false, "Couldn't find wp-plugins.lst (should be in nselib/data)"
end
local wp_autoroot
local wp_root = stdnse.get_script_args("http-wordpress-plugins.root")
local plugins_search = DEFAULT_PLUGINS_SEARCH
local plugins_search_arg = stdnse.get_script_args("http-wordpress-plugins.search")
if plugins_search_arg == "all" then
plugins_search = nil
elseif plugins_search_arg then
plugins_search = tonumber(plugins_search_arg)
end
stdnse.print_debug(1, "%s plugins search range: %s", SCRIPT_NAME, plugins_search or "unlimited")
-- search the website root for evidences of a Wordpress path
if not wp_root then
local target_index = http.get(host,port, "/")
if target_index.status and target_index.body then
wp_autoroot = string.match(target_index.body, "http://[%w%-%.]-/([%w%-%./]-)wp%-content")
if wp_autoroot then
wp_autoroot = "/" .. wp_autoroot
stdnse.print_debug(1, "%s WP root directory: %s", SCRIPT_NAME, wp_autoroot)
else
stdnse.print_debug(1, "%s WP root directory: wp_autoroot was unable to find a WP content dir (root page returns %d).", SCRIPT_NAME, target_index.status)
end
end
end
--identify the 404
local status_404, result_404, body_404 = http.identify_404(host, port)
if not status_404 then
return stdnse.format_output(false, SCRIPT_NAME .. " unable to handle 404 pages (" .. result_404 .. ")")
end
--build a table of both directories to brute force and the corresponding WP plugins' name
local plugin_count = 0
for line in read_data_file(file) do
if plugins_search and plugin_count >= plugins_search then
break
end
local target
if wp_root then
-- Give user-supplied argument the priority
target = wp_root .. "/wp-content/plugins/" .. line .. "/"
elseif wp_autoroot then
-- Maybe the script has discovered another Wordpress content directory
target = wp_autoroot .. "wp-content/plugins/" .. line .. "/"
else
-- Default WP directory is root
target = "/wp-content/plugins/" .. line .. "/"
end
target = string.gsub(target, "//", "/")
table.insert(bfqueries, {target, line})
all = http.pipeline_add(target, nil, all, "GET")
plugin_count = plugin_count 1
end
-- release hell...
local pipeline_returns = http.pipeline_go(host, port, all)
if not pipeline_returns then
stdnse.print_debug(1, "%s : got no answers from pipelined queries", SCRIPT_NAME)
end
for i, data in pairs(pipeline_returns) do
-- if it's not a four-'o-four, it probably means that the plugin is present
if http.page_exists(data, result_404, body_404, bfqueries[i][1], true) then
stdnse.print_debug(1, "http-wordpress-plugins.nse: Found a plugin: %s", bfqueries[i][2])
table.insert(result, bfqueries[i][2])
end
end
if #result > 0 then
result.name = "search amongst the " .. plugin_count .. " most popular plugins"
return stdnse.format_output(true, result)
else
return "nothing found amongst the " .. plugin_count .. " most popular plugins, use --script-args http-wordpress-plugins.search=<number|all> for deeper analysis)\n"
end
end