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
- 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
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
20 21 22 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 20 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
20 21 22 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 20 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
20 21 22 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 20 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
116 117 118 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 116 def do_login(username, password) login_request(username, password).run end |
#login_request(username, password) ⇒ Typhoeus::Request
124 125 126 127 128 129 130 131 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 124 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
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.
138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 138 def login_url return @login_url if @login_url @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 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
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 70 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
93 94 95 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 93 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 |
# 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 [homepage_res, error_404_res].each do |page_res| in_scope_uris(page_res) 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
123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/wpscan/target/platform/wordpress/custom_directories.rb', line 123 def url(path = nil) return @uri.to_s unless path if %r{wp\-content/plugins}i.match?(path) path = +path.gsub('wp-content/plugins', plugins_dir) elsif /wp\-content/i.match?(path) path = +path.gsub('wp-content', content_dir) elsif path[0] != '/' && sub_dir path = "#{sub_dir}/#{path}" end super(path) end |
#wordpress?(detection_mode) ⇒ Boolean
Returns Whether or not the target is running WordPress.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 28 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| return true if in_scope_uris(Browser.get_and_follow_location(url(path))).any? do |uri| WORDPRESS_PATTERN.match?(uri.path) end end end false end |
#wordpress_from_meta_comments_or_scripts?(response) ⇒ Boolean
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 46 def (response) in_scope_uris(response) 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.
98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/wpscan/target/platform/wordpress.rb', line 98 def wordpress_hosted? return true if /\.wordpress\.com$/i.match?(uri.host) unless content_dir pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze uris_from_page(homepage_res) do |uri| return true if uri.to_s.match?(pattern) end end false end |