module WPScan::Target::Platform::WordPress
Some WordPress
specific implementation
wp-content & plugins directory implementation
Constants
- COOKIE_PATTERNS
- WORDPRESS_HOSTED_PATTERN
- WORDPRESS_PATTERN
- WP_ADMIN_AJAX_PATTERN
- WP_JSON_OEMBED_PATTERN
Attributes
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
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
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
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
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
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
Public Instance Methods
@return [ String ] The wp-content directory
# 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
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 8 def content_dir=(dir) @content_dir = dir.chomp('/') end
@return [ Addressable::URI ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 46 def content_uri uri.join("#{content_dir}/") end
@return [ String ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 51 def content_url content_uri.to_s end
# 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
@param [ String ] username @param [ String ] password
@return [ Typhoeus::Response
]
# File lib/wpscan/target/platform/wordpress.rb, line 119 def do_login(username, password) login_request(username, password).run end
@param [ String ] username @param [ String ] password
@return [ Typhoeus::Request ]
# 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
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.
@return [ String, false ] The URL to the login page or false if not detected
# 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
@param [ String ] slug
@return [ String ]
# 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
@return [ String ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 56 def plugins_dir @plugins_dir ||= "#{content_dir}/plugins" end
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 12 def plugins_dir=(dir) @plugins_dir = dir.chomp('/') end
@return [ Addressable::URI ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 61 def plugins_uri uri.join("#{plugins_dir}/") end
@return [ String ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 66 def plugins_url plugins_uri.to_s end
@return [ String ]
# File lib/wpscan/target/platform/wordpress.rb, line 98 def registration_url multisite? ? url('wp-signup.php') : url('wp-login.php?action=register') end
@return [ String, False ] String of the sub_dir
found, false otherwise @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
# 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
@param [ String ] slug
@return [ String ]
# 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
@return [ String ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 78 def themes_dir @themes_dir ||= "#{content_dir}/themes" end
@return [ Addressable::URI ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 83 def themes_uri uri.join("#{themes_dir}/") end
@return [ String ]
# File lib/wpscan/target/platform/wordpress/custom_directories.rb, line 88 def themes_url themes_uri.to_s end
Override of the WebSite#url to consider the custom WP directories
@param [ String ] path Optional path to merge with the uri
@return [ String ]
# 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
@param [ Symbol ] detection_mode
@return [ Boolean ] Whether or not the target is running WordPress
# 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 wordpress_from_meta_comments_or_scripts?(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
@param [ Typhoeus::Response
] response @return [ Boolean ]
# File lib/wpscan/target/platform/wordpress.rb, line 51 def wordpress_from_meta_comments_or_scripts?(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
@return [ Boolean ] Whether or not the target is hosted on wordpress.com
# 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