Class: WPScan::Target
- Includes:
- Platform::WordPress, Server::Generic
- Defined in:
- lib/wpscan/target.rb,
lib/wpscan/target/scope.rb,
lib/wpscan/target/hashes.rb,
lib/wpscan/target/server/iis.rb,
lib/wpscan/target/platform/php.rb,
lib/wpscan/target/server/nginx.rb,
lib/wpscan/target/server/apache.rb,
lib/wpscan/target/server/generic.rb,
lib/wpscan/target/platform/wordpress.rb,
lib/wpscan/target/platform/wordpress/custom_directories.rb
Overview
Scope system logic
Defined Under Namespace
Modules: Platform, Server Classes: Scope
Constant Summary
Constants included from Platform::WordPress
Platform::WordPress::COOKIE_PATTERNS, Platform::WordPress::WORDPRESS_HOSTED_PATTERN, Platform::WordPress::WORDPRESS_PATTERN, Platform::WordPress::WP_ADMIN_AJAX_PATTERN, Platform::WordPress::WP_JSON_OEMBED_PATTERN
Constants included from Platform::PHP
Platform::PHP::DEBUG_LOG_PATTERN, Platform::PHP::ERROR_LOG_PATTERN, Platform::PHP::FPD_PATTERN
Instance Attribute Summary
Attributes included from Platform::WordPress
#mu_plugins, #multisite, #registration_enabled
Attributes inherited from WebSite
Class Method Summary collapse
-
.page_hash(page) ⇒ String
The md5sum of the page.
Instance Method Summary collapse
- #backup_folders(opts = {}) ⇒ Array<BackupFolder>
- #comments_from_page(pattern, page = nil) {|MatchData, Nokogiri::XML::Comment| ... } ⇒ Array<Array<MatchData, Nokogiri::XML::Comment>>
- #config_backups(opts = {}) ⇒ Array<ConfigBackup>
- #db_exports(opts = {}) ⇒ Array<DBExport>
-
#error_404_hash ⇒ String
The hash of a 404.
- #head_or_get_request_params ⇒ Hash
-
#homepage_hash ⇒ String
The hash of the homepage.
-
#homepage_or_404?(page) ⇒ Boolean
Wether or not the page is a the homepage or a 404 based on its md5sum.
-
#in_scope?(url_or_uri) ⇒ Boolean
True if the url given is in scope.
-
#in_scope_uris(res, xpath = '//@href|//@src|//@data-src') {|Addressable::URI, Nokogiri::XML::Element| ... } ⇒ Array<Addressable::URI>
The in scope absolute URIs detected in the response’s body.
-
#initialize(url, opts = {}) ⇒ Target
constructor
A new instance of Target.
- #interesting_findings(opts = {}) ⇒ Findings
- #javascripts_from_page(pattern, page = nil) {|MatchData, Nokogiri::XML::Element| ... } ⇒ Array<Array<MatchData, Nokogiri::XML::Element>>
- #main_theme(opts = {}) ⇒ Theme
- #medias(opts = {}) ⇒ Array<Media>
- #plugins(opts = {}) {|Plugin| ... } ⇒ Array<Plugin>
- #scope ⇒ Array<PublicSuffix::Domain, String>
-
#scope_url_pattern ⇒ Regexp
Similar to Target#url_pattern but considering the in scope domains as well.
- #themes(opts = {}) {|Theme| ... } ⇒ Array<Theme>
- #timthumbs(opts = {}) ⇒ Array<Timthumb>
-
#uris_from_page(page = nil, xpath = '//@href|//@src|//@data-src') {|Addressable::URI, Nokogiri::XML::Element| ... } ⇒ Array<Addressable::URI>
The absolute URIs detected in the response’s body from the HTML tags.
-
#url_pattern ⇒ Regexp
The pattern related to the target url, also matches escaped /, such as in JSON JS data: http://t.com/.
- #users(opts = {}) {|User| ... } ⇒ Array<User>
- #vulnerable? ⇒ Boolean
-
#wp_version(opts = {}) ⇒ WpVersion, false
The WpVersion found or false if not detected.
- #xmlrpc ⇒ XMLRPC?
- #xpath_pattern_from_page(xpath, pattern, page = nil) {|MatchData, Nokogiri::XML::Element| ... } ⇒ Array<Array<MatchData, Nokogiri::XML::Element>>
Methods included from Platform::WordPress
#content_dir, #content_dir=, #content_uri, #content_url, #default_content_dir_exists?, #do_login, #login_request, #login_url, #maybe_add_cookies, #plugin_url, #plugins_dir, #plugins_dir=, #plugins_uri, #plugins_url, #registration_url, #sub_dir, #theme_url, #themes_dir, #themes_uri, #themes_url, #url, #wordpress?, #wordpress_from_meta_comments_or_scripts?, #wordpress_hosted?
Methods included from Platform::PHP
#debug_log?, #error_log?, #full_path_disclosure?, #full_path_disclosure_entries, #install_body_cap, #log_file?, #stream_capped_body
Methods included from Server::Generic
#directory_listing?, #directory_listing_entries, #headers, #server
Methods inherited from WebSite
#access_forbidden?, #error_404_res, #error_404_url, #head_and_get, #head_or_get_params, #homepage_url, #http_auth?, #ip, #online?, #proxy_auth?, #redirection, #reset_homepage_cache!, #url, #url=
Constructor Details
#initialize(url, opts = {}) ⇒ Target
Returns a new instance of Target.
19 20 21 22 23 24 |
# File 'lib/wpscan/target.rb', line 19 def initialize(url, opts = {}) super scope << uri.host Array(opts[:scope]).each { |s| scope << s } end |
Class Method Details
.page_hash(page) ⇒ String
Comments are deleted to avoid cache generation details
Returns The md5sum of the page.
11 12 13 14 15 16 17 18 19 20 |
# File 'lib/wpscan/target/hashes.rb', line 11 def self.page_hash(page) page = WPScan::Browser.get(page, followlocation: true, maxredirs: 10) unless page.is_a?(Typhoeus::Response) # Removes comments and script tags before computing the hash # to remove any potential cached stuff html = Nokogiri::HTML(page.body) html.xpath('//script|//comment()').each(&:remove) Digest::MD5.hexdigest(html) end |
Instance Method Details
#backup_folders(opts = {}) ⇒ Array<BackupFolder>
205 206 207 |
# File 'lib/wpscan/target.rb', line 205 def backup_folders(opts = {}) @backup_folders ||= Finders::BackupFolders::Base.find(self, opts) end |
#comments_from_page(pattern, page = nil) {|MatchData, Nokogiri::XML::Comment| ... } ⇒ Array<Array<MatchData, Nokogiri::XML::Comment>>
93 94 95 96 97 |
# File 'lib/wpscan/target.rb', line 93 def comments_from_page(pattern, page = nil) xpath_pattern_from_page('//comment()', pattern, page) do |match, node| yield match, node if block_given? end end |
#config_backups(opts = {}) ⇒ Array<ConfigBackup>
191 192 193 |
# File 'lib/wpscan/target.rb', line 191 def config_backups(opts = {}) @config_backups ||= Finders::ConfigBackups::Base.find(self, opts) end |
#db_exports(opts = {}) ⇒ Array<DBExport>
198 199 200 |
# File 'lib/wpscan/target.rb', line 198 def db_exports(opts = {}) @db_exports ||= Finders::DbExports::Base.find(self, opts) end |
#error_404_hash ⇒ String
This is used to detect potential custom 404 responding with a 200
Returns The hash of a 404.
29 30 31 |
# File 'lib/wpscan/target/hashes.rb', line 29 def error_404_hash @error_404_hash ||= self.class.page_hash(error_404_res) end |
#head_or_get_request_params ⇒ Hash
27 28 29 30 31 32 33 |
# File 'lib/wpscan/target.rb', line 27 def head_or_get_request_params @head_or_get_request_params ||= if Browser.head(url).code == 405 { method: :get, maxfilesize: 1 } else { method: :head } end end |
#homepage_hash ⇒ String
Returns The hash of the homepage.
23 24 25 |
# File 'lib/wpscan/target/hashes.rb', line 23 def homepage_hash @homepage_hash ||= self.class.page_hash(url) end |
#homepage_or_404?(page) ⇒ Boolean
Returns Wether or not the page is a the homepage or a 404 based on its md5sum.
35 36 37 |
# File 'lib/wpscan/target/hashes.rb', line 35 def homepage_or_404?(page) homepage_and_404_hashes.include?(self.class.page_hash(page)) end |
#in_scope?(url_or_uri) ⇒ Boolean
Returns true if the url given is in scope.
14 15 16 17 18 19 20 |
# File 'lib/wpscan/target/scope.rb', line 14 def in_scope?(url_or_uri) url_or_uri = Addressable::URI.parse(url_or_uri.strip) unless url_or_uri.is_a?(Addressable::URI) scope.include?(url_or_uri.host) rescue StandardError false end |
#in_scope_uris(res, xpath = '//@href|//@src|//@data-src') {|Addressable::URI, Nokogiri::XML::Element| ... } ⇒ Array<Addressable::URI>
It is highly recommended to use the xpath parameter to focus on the uris needed, as this method can be quite time consuming when there are a lof of uris to check
Returns The in scope absolute URIs detected in the response’s body.
31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/wpscan/target/scope.rb', line 31 def in_scope_uris(res, xpath = '//@href|//@src|//@data-src') found = [] uris_from_page(res, xpath) do |uri, tag| next unless in_scope?(uri) yield uri, tag if block_given? found << uri end found end |
#interesting_findings(opts = {}) ⇒ Findings
52 53 54 |
# File 'lib/wpscan/target.rb', line 52 def interesting_findings(opts = {}) @interesting_findings ||= WPScan::Finders::InterestingFindings::Base.find(self, opts) end |
#javascripts_from_page(pattern, page = nil) {|MatchData, Nokogiri::XML::Element| ... } ⇒ Array<Array<MatchData, Nokogiri::XML::Element>>
104 105 106 107 108 |
# File 'lib/wpscan/target.rb', line 104 def javascripts_from_page(pattern, page = nil) xpath_pattern_from_page('//script', pattern, page) do |match, node| yield match, node if block_given? end end |
#main_theme(opts = {}) ⇒ Theme
157 158 159 160 161 |
# File 'lib/wpscan/target.rb', line 157 def main_theme(opts = {}) @main_theme = Finders::MainTheme::Base.find(self, opts) if @main_theme.nil? @main_theme end |
#medias(opts = {}) ⇒ Array<Media>
212 213 214 |
# File 'lib/wpscan/target.rb', line 212 def medias(opts = {}) @medias ||= Finders::Medias::Base.find(self, opts) end |
#plugins(opts = {}) {|Plugin| ... } ⇒ Array<Plugin>
168 169 170 |
# File 'lib/wpscan/target.rb', line 168 def plugins(opts = {}, &) @plugins ||= Finders::Plugins::Base.find(self, opts, &) end |
#scope ⇒ Array<PublicSuffix::Domain, String>
7 8 9 |
# File 'lib/wpscan/target/scope.rb', line 7 def scope @scope ||= Scope.new end |
#scope_url_pattern ⇒ Regexp
Similar to Target#url_pattern but considering the in scope domains as well
rubocop:disable Metrics/AbcSize
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/wpscan/target/scope.rb', line 50 def scope_url_pattern return @scope_url_pattern if @scope_url_pattern domains = [uri.host + uri.path] domains += if scope.domains.empty? Array(scope.invalid_domains[1..]) else Array(scope.domains[1..]).map(&:to_s) + scope.invalid_domains end domains.map! { |d| Regexp.escape(d.delete_suffix('/')).gsub('\*', '.*').gsub('/', '\\\\\?/') } domains[0].gsub!(Regexp.escape(uri.host), "#{Regexp.escape(uri.host)}(?::\\d+)?") if uri.port @scope_url_pattern = %r{https?:\\?/\\?/(?:#{domains.join('|')})\\?/?}i end |
#themes(opts = {}) {|Theme| ... } ⇒ Array<Theme>
177 178 179 |
# File 'lib/wpscan/target.rb', line 177 def themes(opts = {}, &) @themes ||= Finders::Themes::Base.find(self, opts, &) end |
#timthumbs(opts = {}) ⇒ Array<Timthumb>
184 185 186 |
# File 'lib/wpscan/target.rb', line 184 def timthumbs(opts = {}) @timthumbs ||= Finders::Timthumbs::Base.find(self, opts) end |
#uris_from_page(page = nil, xpath = '//@href|//@src|//@data-src') {|Addressable::URI, Nokogiri::XML::Element| ... } ⇒ Array<Addressable::URI>
It is highly recommended to use the xpath parameter to focus on the uris needed, as this method can be quite time consuming when there are a lof of uris to check
Returns The absolute URIs detected in the response’s body from the HTML tags.
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 |
# File 'lib/wpscan/target.rb', line 119 def uris_from_page(page = nil, xpath = '//@href|//@src|//@data-src') page = WPScan::Browser.get(url(page)) unless page.is_a?(Typhoeus::Response) found = [] page.html.xpath(xpath).each do |node| attr_value = node.text.to_s next unless attr_value && !attr_value.empty? node_uri = begin uri.join(attr_value.strip) rescue StandardError # Skip potential malformed URLs etc. next end next unless node_uri.host yield node_uri, node.parent if block_given? && !found.include?(node_uri) found << node_uri end found.uniq end |
#url_pattern ⇒ Regexp
Returns The pattern related to the target url, also matches escaped /, such as in JSON JS data: http://t.com/.
63 64 65 |
# File 'lib/wpscan/target.rb', line 63 def url_pattern @url_pattern ||= Regexp.new(Regexp.escape(url).gsub(/https?/i, 'https?').gsub('/', '\\\\\?/'), Regexp::IGNORECASE) end |
#users(opts = {}) {|User| ... } ⇒ Array<User>
221 222 223 |
# File 'lib/wpscan/target.rb', line 221 def users(opts = {}, &) @users ||= Finders::Users::Base.find(self, opts, &) end |
#vulnerable? ⇒ Boolean
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/wpscan/target.rb', line 36 def vulnerable? [@wp_version, @main_theme, @plugins, @themes, @timthumbs].each do |e| Array(e).each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation end return true unless Array(@config_backups).empty? return true unless Array(@db_exports).empty? Array(@users).each { |u| return true if u.password } false end |
#wp_version(opts = {}) ⇒ WpVersion, false
Returns The WpVersion found or false if not detected.
148 149 150 151 152 |
# File 'lib/wpscan/target.rb', line 148 def wp_version(opts = {}) @wp_version = Finders::WpVersion::Base.find(self, opts) if @wp_version.nil? @wp_version end |
#xmlrpc ⇒ XMLRPC?
57 58 59 |
# File 'lib/wpscan/target.rb', line 57 def xmlrpc @xmlrpc ||= interesting_findings&.grep(Model::XMLRPC)&.first end |
#xpath_pattern_from_page(xpath, pattern, page = nil) {|MatchData, Nokogiri::XML::Element| ... } ⇒ Array<Array<MatchData, Nokogiri::XML::Element>>
73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/wpscan/target.rb', line 73 def xpath_pattern_from_page(xpath, pattern, page = nil) page = WPScan::Browser.get(url(page)) unless page.is_a?(Typhoeus::Response) matches = [] page.html.xpath(xpath).each do |node| next unless node.text.strip =~ pattern yield Regexp.last_match, node if block_given? matches << [Regexp.last_match, node] end matches end |