-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathhttp-wordpress-info.nse
227 lines (207 loc) · 8.73 KB
/
http-wordpress-info.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
description = [[
Finds the WordPress version, theme and plugins observed in the page response.
- WordPress version tests for a meta generator html tag, if this is not found an attempt
is made to match version in page HTML or /feed/atom/ a default page in all versions of WordPress.
- Theme is determined by searching HTML resposne for /wp-content/themes/$themename
- Discovered plugins are those that match /wp-content/plugins/$pluginname in the HTML
response. This will not find all plugins, to find all plugins you will need the
http-wordpress-plugins nse script to brute force the plugin paths.
- Additional checks are performed to match comments or other identifiers in the HTML for known plugins.
Original script based on code from Michael Kohl's http-generator.nse
]]
author = "Peter Hill <[email protected]>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
---
-- @usage
-- nmap --script http-wordpress-info [--script-args http-wordpress-info.path=<path>,http-wordpress-info.redirects=<number>,...] <host>
--
-- @output
-- PORT STATE SERVICE
-- 80/tcp open http
-- | http-wordpress-info:
-- | version: WordPress 4.0
-- | theme: canvas
-- | plugins:
-- | w3-total-cache
-- |_ simple-tooltips
-- @args http-wordpress-info.path Specify the path you want to check for a generator meta tag (default to '/').
-- @args http-wordpress-info.redirects Specify the maximum number of redirects to follow (defaults to 3).
portrule = shortport.http
local follow_redirects = function(host, port, path, n)
local loc
local pattern = "^[hH][tT][tT][pP]/1.[01] 30[12]"
local response = http.get(host, port, path)
while (response['status-line'] or ""):match(pattern) and n > 0 do
n = n - 1
loc = response.header['location']
response = http.get_url(loc)
end
return response, loc
end
-- unique key for plugin slug
-- can check multiple matches in value {}
local plugin_patterns = {
["autoptimize"] = {"autoptimize/js","autoptimize/css"},
["w3-total-cache"] = {"by W3 Total Cache"},
["wp-super-cache"] = {" generated by WP-Super-Cache"},
["wordpress-seo"] = {"optimized with the Yoast SEO plugin"},
["wordpress-seo-premium"] = {"optimized with the Yoast SEO Premium plugin"},
["all-in-one-seo-pack"] = {"-- All in One SEO Pack"},
["ubermenu"] = {"UberMenu CSS"},
["hyper-cache"] = {"-- hyper cache 20"},
["cookie-notice"] = {"Cookie Notice plugin"},
["imp-download"] = {"Powered by iMP Download"},
["optinmonster"] = {"-- This site is converting visitors into subscribers and customers with OptinMonster"},
["duracelltomi-google-tag-manager"] = {"for WordPress by gtm4wp.com"},
["woocommerce"] = {"generator[\"\'] content=[\"\']WooCommerce"},
["custom-css-js"] = {"Simple Custom CSS and JS"},
["simple-social-buttons"] = {"Tags generated by Simple Social Buttons"},
["contact-form-7"] = {"var wpcf7 ="},
["jetpack"] = {"-- Jetpack Open Graph Tags --","Jetpack Site Verification Tags --","jetpack.js"},
["wp-rocket"] = {"-- This website is like a Rocket,"},
["autodescription"] = {"The SEO Framework by Sybre Waaijer --"},
["slider-revolution"] = {"--><p class=\"rs-p-wp","END REVOLUTION SLIDER"},
["ninja-forms"] = {"ninja-forms-req-symbol"},
["wp-fastest-cache"] = {"-- WP Fastest Cache file was created"},
["easy-digital-downloads"] = {"var edd_scripts=", "content=\"Easy Digital Downloads"},
["site-origin-panels"] = {"siteorigin-panels"},
["litespeed-cache"] = {"litespeed-webfont-lib"},
["elementor"] = {'class="elementor-'},
["wordfence"] = {'WordfenceTestMonBot'},
["divi-pagebuilder"] = {'.et_pb_column','.et_pb_section','.et_pb_row'},
["addthis-share-buttons"] = {'-- AddThis Share Buttons'},
["fusion-builder"] = {'fusion-builder-row'},
["revslider"] = {'revslider'},
["easy-digital-downloads"] = {"generator[\"\'] content=[\"\']Easy Digital Downloads","edd[\"\']:[\"\']Easy Digital Downloads"},
["gravityforms"] = {"gravityForms", "Gravity Forms"},
["LayerSlider"] = {"layerSlider"},
["masterslider"] = {"MasterSlider"}
}
-- find plugins in HTML page source and return table
function parse_plugins_response (data, plugin_data)
local result = {}
local plugins_uniq = {}
local pluginmatch = 'wp%-content/plugins/([0-9a-z%-.]+)'
for plugin in string.gmatch(data, pluginmatch) do
--table.insert(result, plugin)
plugins_uniq[plugin] = plugin
end
for plugin, plugin_patterns in pairs(plugin_data) do
for x, plugin_pattern in pairs(plugin_patterns) do
if data:match(plugin_pattern) then
plugins_uniq[plugin] = plugin
-- table.insert(result, plugin)
end
end
end
for _, p in pairs(plugins_uniq) do
table.insert(result, p)
end
return result
end
function wp_version_check (response, host, port, path, loc)
local wpver
-- default patterns in page that match WordPress Core Version
local patterns = {'<meta name=[\"\']generator[\"\'] content=[\"\']WordPress ([.0-9]+)[\"\'] ?/?>',
'wp-includes/js/wp-embed.min.js?ver=([.0-9]+)',
'wp-includes/js/comment-reply.min.js?ver=([.0-9]+)',
'wp-emoji-release.min.js?ver=([.0-9]+)',
'wp-includes/css/dist/block-library/style.min.css?ver=([.0-9]+)'}
for key, pattern in pairs(patterns) do
-- make pattern case-insensitive
pattern = pattern:gsub("%a", function (c)
return string.format("[%s%s]", string.lower(c),
string.upper(c))
end)
if wpver == nil then
wpver = response.body:match(pattern)
end
end
-- Find version in /feed/atom/ default XML feed if no other match in html
if ( not wpver and response.body:match("wp%-")) then
local feedpattern = 'generator uri="https://wordpress.org/" version="([.0-9]*)'
feedpath = path .. 'feed/atom/'
-- if redirect occured get feed from loc
if not loc then
feedresponse = http.get(host, port, feedpath)
else
feedresponse = http.get_url(loc)
end
if ( feedresponse and feedresponse.body ) then
wpver = feedresponse.body:match(feedpattern)
end
end
return wpver
end
-- php version from headers only
-- http-php-version.nse makes additional requests
function php_version_check (response)
local phpmatch = 'PHP/([0-9.]+)'
local phpver
for name, value in pairs(response.header) do
if value:match(phpmatch) then
phpver = value:match(phpmatch)
end
end
return(phpver)
end
-- server header only (good if not using -sV to increase speed)
function server_check (headers)
local servermatch = '([a-zA-Z%-%/%)%(0-9.% ]+)'
local server
for name, value in pairs(headers) do
if value:match(servermatch) and name == "server" then
server = value:match(servermatch)
end
end
return(server)
end
action = function(host, port)
local response, loc, generator, testcheck, plugins, themes, wpversion, phpversion, servercheck
local path = stdnse.get_script_args('http-wordpress-info.path') or '/'
local redirects = stdnse.get_script_args('http-wordpress-info.redirects') or 3
local output_tab = stdnse.output_table()
-- Find Version in "meta generator tag"
local themematch = 'wp%-content/themes/([0-9a-zA-Z%-]+)'
-- Get HTML to check for WordPress installation
response, loc = follow_redirects( host, port, path, redirects )
stdnse.debug1("HTTP Status: %s", response["status-line"])
output_tab.status = response["status"]
output_tab.redirect = loc
if ( response and response.body ) then
wpversion = wp_version_check(response, host, port, path, loc)
themes = response.body:match(themematch)
plugins = parse_plugins_response(response.body, plugin_patterns)
phpversion = php_version_check(response)
servercheck = server_check(response.header)
-- Store results in output table
-- output_tab.testing = feedresponse.body
if wpversion then
output_tab.version = 'WordPress ' .. wpversion
else
if (response.body:match("wp%-json") or response.body:match("wp%-admin") or response.body:match("wp%-includes") or response.body:match("wp%-content")) and not wpversion then
output_tab.version = 'WordPress ???'
end
end
if ( themes and #themes > 0 ) then
output_tab.theme = themes
end
if ( plugins and #plugins > 0 ) then
output_tab.plugins = plugins
end
if ( phpversion ) then
output_tab.php = phpversion
end
if ( servercheck ) then
output_tab.server = servercheck
end
end
if ( output_tab.version or output_tab.plugins or output_tab.theme or output_tab.status ) then
return output_tab
end
end