mirror of
https://github.com/nmap/nmap.git
synced 2025-12-11 02:09:03 +00:00
Added the http-wp-plugins script by Ange Gutek.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] Added http-wp-plugins.nse, which retrieves the list of installed
|
||||||
|
Wordpress plugins by bruteforcing the wp-content directory. [Ange Gutek]
|
||||||
|
|
||||||
o [Ndiff] Added nmaprun element information to the diff. [Daniel
|
o [Ndiff] Added nmaprun element information to the diff. [Daniel
|
||||||
Miller]
|
Miller]
|
||||||
|
|
||||||
|
|||||||
14170
nselib/data/wp-plugins.lst
Normal file
14170
nselib/data/wp-plugins.lst
Normal file
File diff suppressed because it is too large
Load Diff
154
scripts/http-wp-plugins.nse
Normal file
154
scripts/http-wp-plugins.nse
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
description = [[
|
||||||
|
Tries to give a list of installed WordPress 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-wp-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-wp-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-wp-plugins --script-arg http-wp-plugins.root="/blog/",http-wp-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-wp-plugins: search amongst the 500 most popular plugins:
|
||||||
|
-- |_akismet, wp-db-backup, all-in-one-seo-pack, stats, wp-to-twitter
|
||||||
|
|
||||||
|
author = "Ange Gutek <ange.gutek@gmail.com>"
|
||||||
|
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
|
||||||
|
categories = {"discovery", "intrusive"}
|
||||||
|
|
||||||
|
require 'http'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'shortport'
|
||||||
|
|
||||||
|
local DEFAULT_PLUGINS_SEARCH = 100
|
||||||
|
|
||||||
|
|
||||||
|
portrule = shortport.service("http")
|
||||||
|
|
||||||
|
|
||||||
|
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-wp-plugins.root")
|
||||||
|
local plugins_search = DEFAULT_PLUGINS_SEARCH
|
||||||
|
local plugins_search_arg = stdnse.get_script_args("http-wp-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
|
||||||
|
stdnse.print_debug(1, "%s unable to handle 404 pages (%s)", SCRIPT_NAME, result_404)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--build a table of both directories to brute force and the corresponding WP plugins' name
|
||||||
|
local plugin_count = 0
|
||||||
|
while true do
|
||||||
|
local line = file:read()
|
||||||
|
if not line then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
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-wp-plugins.nse: Found a plugin: %s", bfqueries[i][2])
|
||||||
|
table.insert(result, bfqueries[i][2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if table.getn(result) > 0 then
|
||||||
|
local output_str = stdnse.strjoin(", ", result)
|
||||||
|
return "search amongst the " .. plugin_count .. " most popular plugins:\n" .. output_str
|
||||||
|
else
|
||||||
|
return "nothing found amongst the " .. plugin_count .. " most popular plugins, use --script-arg http-wp-plugins.search=<number|all> for deeper analysis)\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
@@ -74,6 +74,7 @@ Entry { filename = "http-trace.nse", categories = { "discovery", "safe", } }
|
|||||||
Entry { filename = "http-userdir-enum.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "http-userdir-enum.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "http-vhosts.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "http-vhosts.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "http-vmware-path-vuln.nse", categories = { "default", "safe", "vuln", } }
|
Entry { filename = "http-vmware-path-vuln.nse", categories = { "default", "safe", "vuln", } }
|
||||||
|
Entry { filename = "http-wp-plugins.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "iax2-version.nse", categories = { "version", } }
|
Entry { filename = "iax2-version.nse", categories = { "version", } }
|
||||||
Entry { filename = "imap-capabilities.nse", categories = { "default", "safe", } }
|
Entry { filename = "imap-capabilities.nse", categories = { "default", "safe", } }
|
||||||
Entry { filename = "informix-brute.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "informix-brute.nse", categories = { "auth", "intrusive", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user