Module: WPScan::Target::Platform::WordPress
- Includes:
- CMSScanner::Target::Platform::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.freeze
- WORDPRESS_HOSTED_PATTERN =
%r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
- WP_JSON_OEMBED_PATTERN =
%r{/wp-json/oembed/}i.freeze
- WP_ADMIN_AJAX_PATTERN =
%r{\\?/wp-admin\\?/admin-ajax\.php}i.freeze
- COOKIE_PATTERNS =
{ 'vjs' => /createCookie\('vjs','(?<c_value>\d+)',\d+\);/i }.freeze
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
-
#content_dir ⇒ String
The wp-content directory.
- #content_dir=(dir) ⇒ Object
- #content_uri ⇒ Addressable::URI
- #content_url ⇒ String
- #default_content_dir_exists? ⇒ Boolean
- #do_login(username, password) ⇒ Typhoeus::Response
- #login_request(username, password) ⇒ Typhoeus::Request
-
#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.
-
#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.
- #plugin_url(slug) ⇒ String
- #plugins_dir ⇒ String
- #plugins_dir=(dir) ⇒ Object
- #plugins_uri ⇒ Addressable::URI
- #plugins_url ⇒ String
- #registration_url ⇒ String
-
#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.
- #theme_url(slug) ⇒ String
- #themes_dir ⇒ String
- #themes_uri ⇒ Addressable::URI
- #themes_url ⇒ String
-
#url(path = nil) ⇒ String
Override of the WebSite#url to consider the custom WP directories.
-
#wordpress?(detection_mode) ⇒ Boolean
Whether or not the target is running WordPress.
- #wordpress_from_meta_comments_or_scripts?(response) ⇒ Boolean
-
#wordpress_hosted? ⇒ Boolean
Whether or not the target is hosted on wordpress.com.
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 # scope_url_pattern is from CMSScanner::Target 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 # Checks for the pattern in raw JS code, as well as @content attributes of meta tags 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? # url('wp-content') can't be used here as the folder has not yet been identified # and the method would try to replace it by nil which would raise an error [200, 401, 403].include?(Browser.forge_request(uri.join('wp-content/').to_s, head_or_get_params).run.code) end |
#do_login(username, password) ⇒ Typhoeus::Response
119 120 121 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 119 def do_login(username, password) login_request(username, password).run end |
#login_request(username, password) ⇒ Typhoeus::Request
127 128 129 130 131 132 133 134 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 127 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.
143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 143 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 95 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 75 def COOKIE_PATTERNS.each do |, pattern| next unless homepage_res.body =~ pattern browser = Browser.instance = "#{}=#{Regexp.last_match[:c_value]}" += "; #{browser.}" if browser. browser. = # Force recheck of the homepage when retying wordpress? # No need to clear the cache, as the request (which will contain the cookies) # will be different @homepage_res = nil @homepage_url = nil 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
98 99 100 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 98 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? # url_pattern is from CMSScanner::Target 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 |
#wordpress_from_meta_comments_or_scripts?(response) ⇒ Boolean
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 comments_from_page(/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.
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 103 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 |