Module: WPScan::Target::Platform::WordPress
- Includes:
- PHP
- Included in:
- WPScan::Target
- Defined in:
- lib/wpscan/target/platform/wordpress.rb,
lib/wpscan/target/platform/wordpress/custom_directories.rb
Overview
wp-content & plugins directory implementation
Constant Summary
collapse
- WORDPRESS_PATTERN =
%r{/(?:(?:wp-content/(?:themes|(?:mu-)?plugins|uploads))|wp-includes)/}i
- WORDPRESS_HOSTED_PATTERN =
%r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i
- WP_JSON_OEMBED_PATTERN =
%r{/wp-json/oembed/}i
- WP_ADMIN_AJAX_PATTERN =
%r{\\?/wp-admin\\?/admin-ajax\.php}i
- COOKIE_PATTERNS =
{
'vjs' => /createCookie\('vjs','(?<c_value>\d+)',\d+\);/i
}.freeze
Constants included
from PHP
PHP::DEBUG_LOG_PATTERN, PHP::ERROR_LOG_PATTERN, PHP::FPD_PATTERN
Instance Attribute Summary collapse
-
#mu_plugins ⇒ Object
(also: #mu_plugins?)
These methods are used in the associated interesting_findings finders to keep the boolean state of the finding rather than re-check the whole thing again.
-
#multisite ⇒ Object
(also: #multisite?)
These methods are used in the associated interesting_findings finders to keep the boolean state of the finding rather than re-check the whole thing again.
-
#registration_enabled ⇒ Object
(also: #registration_enabled?)
These methods are used in the associated interesting_findings finders to keep the boolean state of the finding rather than re-check the whole thing again.
Instance Method Summary
collapse
Methods included from PHP
#debug_log?, #error_log?, #full_path_disclosure?, #full_path_disclosure_entries, #install_body_cap, #log_file?, #stream_capped_body
Instance Attribute Details
#mu_plugins ⇒ Object
Also known as:
mu_plugins?
These methods are used in the associated interesting_findings finders to keep the boolean state of the finding rather than re-check the whole thing again
21
22
23
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 21
def mu_plugins
@mu_plugins
end
|
#multisite ⇒ Object
Also known as:
multisite?
These methods are used in the associated interesting_findings finders to keep the boolean state of the finding rather than re-check the whole thing again
21
22
23
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 21
def multisite
@multisite
end
|
#registration_enabled ⇒ Object
Also known as:
registration_enabled?
These methods are used in the associated interesting_findings finders to keep the boolean state of the finding rather than re-check the whole thing again
21
22
23
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 21
def registration_enabled
@registration_enabled
end
|
Instance Method Details
#content_dir ⇒ String
Returns The wp-content directory.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 17
def content_dir
unless @content_dir
pattern = %r{#{scope_url_pattern}([\w\s\-/]+?)\\?/(?:themes|plugins|uploads|cache)\\?/}i
[homepage_res, error_404_res].each do |page_res|
in_scope_uris(page_res, '//link/@href|//script/@src|//img/@src') do |uri|
return @content_dir = Regexp.last_match[1] if uri.to_s.match(pattern)
end
xpath_pattern_from_page('//script[not(@src)]|//meta/@content', pattern, page_res) do |match|
return @content_dir = match[1]
end
end
return @content_dir = 'wp-content' if default_content_dir_exists?
end
@content_dir
end
|
#content_dir=(dir) ⇒ Object
8
9
10
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 8
def content_dir=(dir)
@content_dir = dir.chomp('/')
end
|
#content_uri ⇒ Addressable::URI
46
47
48
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 46
def content_uri
uri.join("#{content_dir}/")
end
|
#content_url ⇒ String
51
52
53
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 51
def content_url
content_uri.to_s
end
|
#default_content_dir_exists? ⇒ Boolean
39
40
41
42
43
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 39
def default_content_dir_exists?
[200, 401, 403].include?(Browser.forge_request(uri.join('wp-content/').to_s, head_or_get_params).run.code)
end
|
118
119
120
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 118
def do_login(username, password)
login_request(username, password).run
end
|
#login_request(username, password) ⇒ Typhoeus::Request
126
127
128
129
130
131
132
133
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 126
def login_request(username, password)
Browser.instance.forge_request(
login_url,
method: :post,
cache_ttl: 0,
body: { log: username, pwd: password }
)
end
|
#login_url ⇒ String, false
The login page is checked for a potential redirection (from http to https) the first time the method is called, and the effective_url is then used if suitable, otherwise the default wp-login will be.
If the login_uri CLI option has been provided, it will be returne w/o redirection check.
142
143
144
145
146
147
148
149
150
151
152
153
154
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 142
def login_url
return @login_url unless @login_url.nil?
return @login_url = url(ParsedCli.login_uri) if ParsedCli.login_uri
@login_url = url('wp-login.php')
res = Browser.get_and_follow_location(@login_url)
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
@login_url = false if res.code == 404
@login_url
end
|
#maybe_add_cookies ⇒ Object
Sometimes there is a mechanism in place on the blog, which requires a specific cookie and value to be added to requests. Lets try to detect and add them
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 75
def maybe_add_cookies
COOKIE_PATTERNS.each do |cookie_key, pattern|
next unless homepage_res.body =~ pattern
browser = Browser.instance
cookie_string = "#{cookie_key}=#{Regexp.last_match[:c_value]}"
cookie_string += "; #{browser.cookie_string}" if browser.cookie_string
browser.cookie_string = cookie_string
reset_homepage_cache!
break
end
end
|
#plugin_url(slug) ⇒ String
73
74
75
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 73
def plugin_url(slug)
plugins_uri.join("#{Addressable::URI.encode(slug)}/").to_s
end
|
#plugins_dir ⇒ String
56
57
58
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 56
def plugins_dir
@plugins_dir ||= "#{content_dir}/plugins"
end
|
#plugins_dir=(dir) ⇒ Object
12
13
14
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 12
def plugins_dir=(dir)
@plugins_dir = dir.chomp('/')
end
|
#plugins_uri ⇒ Addressable::URI
61
62
63
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 61
def plugins_uri
uri.join("#{plugins_dir}/")
end
|
#plugins_url ⇒ String
66
67
68
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 66
def plugins_url
plugins_uri.to_s
end
|
#registration_url ⇒ String
97
98
99
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 97
def registration_url
multisite? ? url('wp-signup.php') : url('wp-login.php?action=register')
end
|
#sub_dir ⇒ String, False
@note: nil can not be returned here, otherwise if there is no sub_dir
the check would be done each time, which would make enumeration of
long list of items very slow to generate
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 103
def sub_dir
return @sub_dir unless @sub_dir.nil?
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp-includes/)}i
xpath = '(//@src|//@href|//@data-src)[contains(., "xmlrpc.php") or contains(., "wp-includes/")]'
[homepage_res, error_404_res].each do |page_res|
in_scope_uris(page_res, xpath) do |uri|
return @sub_dir = Regexp.last_match[1] if uri.to_s.match(pattern)
end
end
@sub_dir = false
end
|
#theme_url(slug) ⇒ String
95
96
97
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 95
def theme_url(slug)
themes_uri.join("#{Addressable::URI.encode(slug)}/").to_s
end
|
#themes_dir ⇒ String
78
79
80
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 78
def themes_dir
@themes_dir ||= "#{content_dir}/themes"
end
|
#themes_uri ⇒ Addressable::URI
83
84
85
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 83
def themes_uri
uri.join("#{themes_dir}/")
end
|
#themes_url ⇒ String
88
89
90
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 88
def themes_url
themes_uri.to_s
end
|
#url(path = nil) ⇒ String
Override of the WebSite#url to consider the custom WP directories
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 124
def url(path = nil)
return @uri.to_s unless path
if %r{wp-content/plugins}i.match?(path)
new_path = path.gsub('wp-content/plugins', plugins_dir)
elsif /wp-content/i.match?(path)
new_path = path.gsub('wp-content', content_dir)
elsif path[0] != '/' && sub_dir
new_path = "#{sub_dir}/#{path}"
end
super(new_path || path)
end
|
#wordpress?(detection_mode) ⇒ Boolean
Returns Whether or not the target is running WordPress.
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 29
def wordpress?(detection_mode)
[homepage_res, error_404_res].each do |page_res|
return true if (page_res)
end
if %i[mixed aggressive].include?(detection_mode)
%w[wp-admin/install.php wp-login.php].each do |path|
res = Browser.get_and_follow_location(url(path))
next unless res.code == 200
in_scope_uris(res, '//link/@href|//script/@src') do |uri|
return true if WORDPRESS_PATTERN.match?(uri.path)
end
end
end
false
end
|
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 51
def (response)
in_scope_uris(response, '//link/@href|//script/@src') do |uri|
return true if WORDPRESS_PATTERN.match?(uri.path) || WP_JSON_OEMBED_PATTERN.match?(uri.path)
end
return true if response.html.css('meta[name="generator"]').any? do |node|
/wordpress/i.match?(node['content'])
end
return true unless (/wordpress/i, response).empty?
return true if response.html.xpath('//script[not(@src)]').any? do |node|
WP_ADMIN_AJAX_PATTERN.match?(node.text)
end
false
end
|
#wordpress_hosted? ⇒ Boolean
Returns Whether or not the target is hosted on wordpress.com.
102
103
104
105
106
107
108
109
110
111
112
|
# File 'lib/wpscan/target/platform/wordpress.rb', line 102
def wordpress_hosted?
return true if /\.wordpress\.com$/i.match?(uri.host)
unless content_dir
uris_from_page(homepage_res, '(//@href|//@src)[contains(., "wp.com")]') do |uri|
return true if uri.to_s.match?(WORDPRESS_HOSTED_PATTERN)
end
end
false
end
|