wpscan 3.8.28 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +104 -30
- data/app/app.rb +26 -0
- data/app/controllers/aliases.rb +2 -2
- data/app/controllers/authenticated_inventory.rb +43 -0
- data/app/controllers/core/cli_options.rb +151 -0
- data/app/controllers/core.rb +200 -25
- data/app/controllers/custom_directories.rb +1 -1
- data/app/controllers/enumeration/cli_options.rb +21 -31
- data/app/controllers/enumeration/enum_methods.rb +145 -38
- data/app/controllers/enumeration.rb +26 -3
- data/app/controllers/interesting_findings.rb +25 -0
- data/app/controllers/main_theme.rb +1 -1
- data/app/controllers/password_attack.rb +14 -6
- data/app/controllers/vuln_api.rb +9 -3
- data/app/controllers/wp_version.rb +1 -1
- data/app/controllers.rb +1 -0
- data/app/finders/backup_folders/known_locations.rb +66 -0
- data/app/finders/backup_folders.rb +19 -0
- data/app/finders/config_backups/known_filenames.rb +6 -4
- data/app/finders/config_backups.rb +1 -1
- data/app/finders/db_exports/known_locations.rb +16 -14
- data/app/finders/db_exports.rb +1 -1
- data/app/finders/interesting_findings/backup_db.rb +1 -1
- data/app/finders/interesting_findings/debug_log.rb +1 -1
- data/app/finders/interesting_findings/duplicator_installer_log.rb +1 -1
- data/app/finders/interesting_findings/emergency_pwd_reset_script.rb +1 -1
- data/app/finders/interesting_findings/fantastico_fileslist.rb +21 -0
- data/app/finders/interesting_findings/full_path_disclosure.rb +1 -1
- data/app/finders/interesting_findings/headers.rb +17 -0
- data/app/finders/interesting_findings/mu_plugins.rb +1 -1
- data/app/finders/interesting_findings/multisite.rb +1 -1
- data/app/finders/interesting_findings/php_disabled.rb +2 -2
- data/app/finders/interesting_findings/readme.rb +1 -1
- data/app/finders/interesting_findings/registration.rb +1 -1
- data/app/finders/interesting_findings/robots_txt.rb +20 -0
- data/app/finders/interesting_findings/search_replace_db_2.rb +19 -0
- data/app/finders/interesting_findings/tmm_db_migrate.rb +1 -1
- data/app/finders/interesting_findings/upload_directory_listing.rb +1 -1
- data/app/finders/interesting_findings/upload_sql_dump.rb +2 -2
- data/app/finders/interesting_findings/wp_cron.rb +1 -1
- data/app/finders/interesting_findings/xml_rpc.rb +61 -0
- data/app/finders/interesting_findings.rb +13 -4
- data/app/finders/main_theme/css_style_in_homepage.rb +1 -1
- data/app/finders/main_theme/urls_in_homepage.rb +3 -7
- data/app/finders/main_theme/woo_framework_meta_generator.rb +4 -4
- data/app/finders/main_theme.rb +1 -1
- data/app/finders/medias/attachment_brute_forcing.rb +2 -2
- data/app/finders/medias.rb +1 -1
- data/app/finders/passwords/wp_login.rb +2 -2
- data/app/finders/passwords/xml_rpc.rb +2 -2
- data/app/finders/passwords/xml_rpc_multicall.rb +1 -1
- data/app/finders/plugin_version/readme.rb +1 -1
- data/app/finders/plugin_version.rb +1 -1
- data/app/finders/plugins/known_locations.rb +17 -7
- data/app/finders/plugins/urls_in_homepage.rb +3 -7
- data/app/finders/plugins/wp_json_api.rb +85 -0
- data/app/finders/plugins.rb +2 -1
- data/app/finders/theme_version/style.rb +1 -1
- data/app/finders/theme_version/woo_framework_meta_generator.rb +1 -1
- data/app/finders/theme_version.rb +1 -1
- data/app/finders/themes/known_locations.rb +12 -6
- data/app/finders/themes/urls_in_homepage.rb +3 -7
- data/app/finders/themes/wp_json_api.rb +74 -0
- data/app/finders/themes.rb +2 -1
- data/app/finders/timthumb_version/bad_request.rb +1 -1
- data/app/finders/timthumb_version.rb +1 -1
- data/app/finders/timthumbs/known_locations.rb +6 -4
- data/app/finders/timthumbs.rb +1 -1
- data/app/finders/users/author_id_brute_forcing.rb +11 -7
- data/app/finders/users/author_posts.rb +1 -1
- data/app/finders/users/author_sitemap.rb +1 -1
- data/app/finders/users/login_error_messages.rb +1 -1
- data/app/finders/users/oembed_api.rb +3 -1
- data/app/finders/users/wp_json_api.rb +11 -7
- data/app/finders/users.rb +1 -1
- data/app/finders/wp_version/atom_generator.rb +1 -1
- data/app/finders/wp_version/rdf_generator.rb +1 -1
- data/app/finders/wp_version/readme.rb +1 -1
- data/app/finders/wp_version/rss_generator.rb +1 -1
- data/app/finders/wp_version/unique_fingerprinting.rb +2 -2
- data/app/finders/wp_version.rb +1 -1
- data/app/finders.rb +1 -0
- data/app/formatters/cli.rb +79 -0
- data/app/formatters/cli_no_color.rb +9 -0
- data/app/formatters/cli_no_colour.rb +17 -0
- data/app/formatters/json.rb +14 -0
- data/app/formatters/jsonl.rb +29 -0
- data/app/formatters/sarif.rb +311 -0
- data/app/models/backup_folder.rb +39 -0
- data/app/models/fantastico_fileslist.rb +34 -0
- data/app/models/headers.rb +44 -0
- data/app/models/interesting_finding.rb +41 -2
- data/app/models/plugin.rb +8 -2
- data/app/models/robots_txt.rb +31 -0
- data/app/models/search_replace_db_2.rb +17 -0
- data/app/models/theme.rb +9 -2
- data/app/models/timthumb.rb +2 -2
- data/app/models/user.rb +35 -0
- data/app/models/version.rb +49 -0
- data/app/models/wp_item/wordpress_org_data.rb +55 -0
- data/app/models/wp_item.rb +109 -9
- data/app/models/wp_version.rb +2 -2
- data/app/models/xml_rpc.rb +73 -3
- data/app/models.rb +2 -1
- data/app/user_agents.txt +46 -0
- data/app/views/cli/core/banner.erb +3 -3
- data/app/views/cli/core/finished.erb +15 -0
- data/app/views/cli/core/help.erb +4 -0
- data/app/views/cli/core/started.erb +11 -0
- data/app/views/cli/enumeration/backup_folders.erb +11 -0
- data/app/views/cli/enumeration/plugin.erb +13 -0
- data/app/views/cli/enumeration/plugins.erb +1 -12
- data/app/views/cli/enumeration/theme.erb +4 -0
- data/app/views/cli/enumeration/themes.erb +1 -3
- data/app/views/cli/enumeration/user.erb +4 -0
- data/app/views/cli/enumeration/users.erb +1 -3
- data/app/views/cli/finding.erb +1 -1
- data/app/views/cli/interesting_findings/_array.erb +10 -0
- data/app/views/cli/interesting_findings/findings.erb +23 -0
- data/app/views/cli/scan_aborted.erb +5 -0
- data/app/views/cli/update_aborted.erb +5 -0
- data/app/views/cli/vuln_api/status.erb +2 -0
- data/app/views/cli/vulnerability.erb +6 -0
- data/app/views/cli/wp_item.erb +4 -1
- data/app/views/json/core/banner.erb +2 -8
- data/app/views/json/core/finished.erb +13 -0
- data/app/views/json/core/help.erb +4 -0
- data/app/views/json/core/started.erb +10 -0
- data/app/views/json/enumeration/backup_folders.erb +11 -0
- data/app/views/json/enumeration/plugin.erb +15 -0
- data/app/views/json/enumeration/theme.erb +5 -0
- data/app/views/json/enumeration/user.erb +6 -0
- data/app/views/json/finding.erb +8 -2
- data/app/views/json/interesting_findings/findings.erb +24 -0
- data/app/views/json/notice.erb +1 -0
- data/app/views/json/scan_aborted.erb +5 -0
- data/app/views/json/update_aborted.erb +5 -0
- data/app/views/json/vuln_api/status.erb +2 -0
- data/app/views/json/wp_item.erb +4 -1
- data/bin/wpscan +1 -0
- data/lib/opt_parse_validator/config_files_loader_merger/base.rb +26 -0
- data/lib/opt_parse_validator/config_files_loader_merger/json.rb +17 -0
- data/lib/opt_parse_validator/config_files_loader_merger/yml.rb +17 -0
- data/lib/opt_parse_validator/config_files_loader_merger.rb +62 -0
- data/lib/opt_parse_validator/errors.rb +9 -0
- data/lib/opt_parse_validator/hacks.rb +19 -0
- data/lib/opt_parse_validator/opts/alias.rb +28 -0
- data/lib/opt_parse_validator/opts/array.rb +34 -0
- data/lib/opt_parse_validator/opts/base.rb +142 -0
- data/lib/opt_parse_validator/opts/boolean.rb +19 -0
- data/lib/opt_parse_validator/opts/choice.rb +43 -0
- data/lib/opt_parse_validator/opts/credentials.rb +15 -0
- data/lib/opt_parse_validator/opts/directory_path.rb +17 -0
- data/lib/opt_parse_validator/opts/file_path.rb +34 -0
- data/lib/opt_parse_validator/opts/headers.rb +33 -0
- data/lib/opt_parse_validator/opts/integer.rb +15 -0
- data/lib/opt_parse_validator/opts/integer_range.rb +37 -0
- data/lib/opt_parse_validator/opts/multi_choices.rb +135 -0
- data/lib/opt_parse_validator/opts/path.rb +78 -0
- data/lib/opt_parse_validator/opts/positive_integer.rb +16 -0
- data/lib/opt_parse_validator/opts/proxy.rb +7 -0
- data/lib/opt_parse_validator/opts/regexp.rb +14 -0
- data/lib/opt_parse_validator/opts/smart_list.rb +30 -0
- data/lib/opt_parse_validator/opts/string.rb +8 -0
- data/lib/opt_parse_validator/opts/uri.rb +41 -0
- data/lib/opt_parse_validator/opts/url.rb +11 -0
- data/lib/opt_parse_validator/opts.rb +9 -0
- data/lib/opt_parse_validator/version.rb +6 -0
- data/lib/opt_parse_validator.rb +161 -0
- data/lib/wpscan/browser/actions.rb +48 -0
- data/lib/wpscan/browser/options.rb +92 -0
- data/lib/wpscan/browser.rb +87 -2
- data/lib/wpscan/browser_authenticator.rb +64 -0
- data/lib/wpscan/cache/file_store.rb +77 -0
- data/lib/wpscan/cache/typhoeus.rb +25 -0
- data/lib/wpscan/controller.rb +100 -4
- data/lib/wpscan/controllers.rb +78 -3
- data/lib/wpscan/db/dynamic_finders/base.rb +3 -7
- data/lib/wpscan/db/dynamic_finders/plugin.rb +2 -2
- data/lib/wpscan/db/dynamic_finders/wordpress.rb +1 -1
- data/lib/wpscan/db/fingerprints.rb +2 -2
- data/lib/wpscan/db/updater.rb +23 -13
- data/lib/wpscan/db/vuln_api.rb +19 -7
- data/lib/wpscan/db/wp_item.rb +2 -2
- data/lib/wpscan/errors/enumeration.rb +4 -4
- data/lib/wpscan/errors/http.rb +82 -3
- data/lib/wpscan/errors/saml.rb +28 -0
- data/lib/wpscan/errors/scan.rb +14 -0
- data/lib/wpscan/errors/update.rb +11 -3
- data/lib/wpscan/errors/vuln_api.rb +24 -0
- data/lib/wpscan/errors/wordpress.rb +2 -2
- data/lib/wpscan/errors/wp_auth.rb +37 -0
- data/lib/wpscan/errors.rb +4 -3
- data/lib/wpscan/exit_code.rb +25 -0
- data/lib/wpscan/finders/base_finders.rb +45 -0
- data/lib/wpscan/finders/dynamic_finder/finder.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/body_pattern.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/comment.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/header_pattern.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/javascript_var.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/query_parameter.rb +3 -5
- data/lib/wpscan/finders/dynamic_finder/version/xpath.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/wp_items/finder.rb +3 -3
- data/lib/wpscan/finders/dynamic_finder/wp_version.rb +1 -1
- data/lib/wpscan/finders/finder/breadth_first_dictionary_attack.rb +257 -0
- data/lib/wpscan/finders/finder/enumerator.rb +77 -0
- data/lib/wpscan/finders/finder/fingerprinter.rb +48 -0
- data/lib/wpscan/finders/finder/smart_url_checker/findings.rb +33 -0
- data/lib/wpscan/finders/finder/smart_url_checker.rb +60 -0
- data/lib/wpscan/finders/finder/wp_version/smart_url_checker.rb +1 -1
- data/lib/wpscan/finders/finder.rb +78 -0
- data/lib/wpscan/finders/finding.rb +54 -0
- data/lib/wpscan/finders/findings.rb +33 -0
- data/lib/wpscan/finders/independent_finder.rb +33 -0
- data/lib/wpscan/finders/independent_finders.rb +26 -0
- data/lib/wpscan/finders/same_type_finder.rb +19 -0
- data/lib/wpscan/finders/same_type_finders.rb +28 -0
- data/lib/wpscan/finders/unique_finder.rb +19 -0
- data/lib/wpscan/finders/unique_finders.rb +47 -0
- data/lib/wpscan/finders.rb +11 -12
- data/lib/wpscan/formatter/buffer.rb +17 -0
- data/lib/wpscan/formatter.rb +152 -0
- data/lib/wpscan/helper.rb +7 -1
- data/lib/wpscan/http_status_tracking.rb +128 -0
- data/lib/wpscan/numeric.rb +13 -0
- data/lib/wpscan/parsed_cli.rb +31 -2
- data/lib/wpscan/progressbar_null_output.rb +23 -0
- data/lib/wpscan/public_suffix/domain.rb +44 -0
- data/lib/wpscan/references.rb +118 -4
- data/lib/wpscan/scan.rb +127 -0
- data/lib/wpscan/target/hashes.rb +45 -0
- data/lib/wpscan/target/platform/php.rb +124 -0
- data/lib/wpscan/target/platform/wordpress/custom_directories.rb +3 -3
- data/lib/wpscan/target/platform/wordpress.rb +7 -8
- data/lib/wpscan/target/platform.rb +3 -0
- data/lib/wpscan/target/scope.rb +103 -0
- data/lib/wpscan/target/server/apache.rb +27 -0
- data/lib/wpscan/target/server/generic.rb +72 -0
- data/lib/wpscan/target/server/iis.rb +29 -0
- data/lib/wpscan/target/server/nginx.rb +27 -0
- data/lib/wpscan/target/server.rb +6 -0
- data/lib/wpscan/target.rb +129 -9
- data/lib/wpscan/typhoeus/hydra.rb +12 -0
- data/lib/wpscan/typhoeus/response.rb +24 -1
- data/lib/wpscan/version.rb +1 -1
- data/lib/wpscan/vulnerability.rb +49 -3
- data/lib/wpscan/vulnerability_filter.rb +68 -0
- data/lib/wpscan/vulnerable.rb +13 -1
- data/lib/wpscan/web_site.rb +152 -0
- data/lib/wpscan.rb +126 -29
- metadata +362 -20
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
class Target < WebSite
|
|
5
|
+
module Server
|
|
6
|
+
# Generic Server methods
|
|
7
|
+
module Generic
|
|
8
|
+
# @param [ String ] path
|
|
9
|
+
# @param [ Hash ] params The request params
|
|
10
|
+
#
|
|
11
|
+
# @return [ Symbol ] The detected remote server (:Apache, :IIS, :Nginx)
|
|
12
|
+
def server(path = nil, params = {})
|
|
13
|
+
headers = headers(path, params)
|
|
14
|
+
|
|
15
|
+
return unless headers
|
|
16
|
+
|
|
17
|
+
case headers[:server]
|
|
18
|
+
when /\Aapache/i
|
|
19
|
+
:Apache
|
|
20
|
+
when /\AMicrosoft-IIS/i
|
|
21
|
+
:IIS
|
|
22
|
+
when /\Anginx/
|
|
23
|
+
:Nginx
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param [ String ] path
|
|
28
|
+
# @param [ Hash ] params The request params
|
|
29
|
+
#
|
|
30
|
+
# @return [ Hash ] The headers
|
|
31
|
+
def headers(path = nil, params = {})
|
|
32
|
+
# The HEAD method might be rejected by some servers ... maybe switch to GET ?
|
|
33
|
+
WPScan::Browser.head(url(path), params).headers
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param [ String ] path
|
|
37
|
+
# @param [ Hash ] params The request params
|
|
38
|
+
#
|
|
39
|
+
# @return [ Boolean ] true if url(path) has the directory
|
|
40
|
+
# listing enabled, false otherwise
|
|
41
|
+
def directory_listing?(path = nil, params = {})
|
|
42
|
+
res = WPScan::Browser.get(url(path), params)
|
|
43
|
+
|
|
44
|
+
res.code == 200 && res.body.include?('<h1>Index of')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param [ String ] path
|
|
48
|
+
# @param [ Hash ] params The request params
|
|
49
|
+
# @param [ String ] selector
|
|
50
|
+
# @param [ Regexp ] ignore
|
|
51
|
+
#
|
|
52
|
+
# @return [ Array<String> ] The first level of directories/files listed,
|
|
53
|
+
# or an empty array if none
|
|
54
|
+
def directory_listing_entries(path = nil, params = {}, selector = 'pre a', ignore = /parent directory/i)
|
|
55
|
+
return [] unless directory_listing?(path, params)
|
|
56
|
+
|
|
57
|
+
found = []
|
|
58
|
+
|
|
59
|
+
WPScan::Browser.get(url(path), params).html.css(selector).each do |node|
|
|
60
|
+
entry = node.text.to_s
|
|
61
|
+
|
|
62
|
+
next if entry&.match?(ignore)
|
|
63
|
+
|
|
64
|
+
found << entry
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
found
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
class Target < WebSite
|
|
5
|
+
module Server
|
|
6
|
+
# Some IIS specific implementation
|
|
7
|
+
module IIS
|
|
8
|
+
# @param [ String ] path
|
|
9
|
+
# @param [ Hash ] params The request params
|
|
10
|
+
#
|
|
11
|
+
# @return [ Symbol ] :IIS
|
|
12
|
+
def server(_path = nil, _params = {})
|
|
13
|
+
:IIS
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param [ String ] path
|
|
17
|
+
# @param [ Hash ] params The request params
|
|
18
|
+
#
|
|
19
|
+
# @return [ Boolean ] true if url(path) has the directory
|
|
20
|
+
# listing enabled, false otherwise
|
|
21
|
+
def directory_listing?(path = nil, params = {})
|
|
22
|
+
res = WPScan::Browser.get(url(path), params)
|
|
23
|
+
|
|
24
|
+
res.code == 200 && res.body =~ %r{<H1>#{uri.host} - /} ? true : false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
class Target < WebSite
|
|
5
|
+
module Server
|
|
6
|
+
# Some Nginx specific implementation
|
|
7
|
+
module Nginx
|
|
8
|
+
# @param [ String ] path
|
|
9
|
+
# @param [ Hash ] params The request params
|
|
10
|
+
#
|
|
11
|
+
# @return [ Symbol ] :Nginx
|
|
12
|
+
def server(_path = nil, _params = {})
|
|
13
|
+
:Nginx
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param [ String ] path
|
|
17
|
+
# @param [ Hash ] params The request params
|
|
18
|
+
#
|
|
19
|
+
# @return [ Array<String> ] The first level of directories/files listed,
|
|
20
|
+
# or an empty array if none
|
|
21
|
+
def directory_listing_entries(path = nil, params = {})
|
|
22
|
+
super(path, params, 'pre a', /\A\.\./i)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/wpscan/target.rb
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'wpscan/web_site'
|
|
4
|
+
require 'wpscan/target/platform'
|
|
5
|
+
require 'wpscan/target/server'
|
|
6
|
+
require 'wpscan/target/scope'
|
|
7
|
+
require 'wpscan/target/hashes'
|
|
3
8
|
require 'wpscan/target/platform/wordpress'
|
|
4
9
|
|
|
5
10
|
module WPScan
|
|
6
|
-
#
|
|
7
|
-
class Target <
|
|
11
|
+
# Target to Scan (WordPress).
|
|
12
|
+
class Target < WebSite
|
|
13
|
+
include Server::Generic
|
|
8
14
|
include Platform::WordPress
|
|
9
15
|
|
|
16
|
+
# @param [ String ] url
|
|
17
|
+
# @param [ Hash ] opts
|
|
18
|
+
# @option opts [ Array<PublicSuffix::Domain, String> ] :scope
|
|
19
|
+
def initialize(url, opts = {})
|
|
20
|
+
super
|
|
21
|
+
|
|
22
|
+
scope << uri.host
|
|
23
|
+
Array(opts[:scope]).each { |s| scope << s }
|
|
24
|
+
end
|
|
25
|
+
|
|
10
26
|
# @return [ Hash ]
|
|
11
27
|
def head_or_get_request_params
|
|
12
28
|
@head_or_get_request_params ||= if Browser.head(url).code == 405
|
|
@@ -30,9 +46,100 @@ module WPScan
|
|
|
30
46
|
false
|
|
31
47
|
end
|
|
32
48
|
|
|
49
|
+
# @param [ Hash ] opts
|
|
50
|
+
#
|
|
51
|
+
# @return [ Findings ]
|
|
52
|
+
def interesting_findings(opts = {})
|
|
53
|
+
@interesting_findings ||= WPScan::Finders::InterestingFindings::Base.find(self, opts)
|
|
54
|
+
end
|
|
55
|
+
|
|
33
56
|
# @return [ XMLRPC, nil ]
|
|
34
57
|
def xmlrpc
|
|
35
|
-
@xmlrpc ||= interesting_findings&.
|
|
58
|
+
@xmlrpc ||= interesting_findings&.grep(Model::XMLRPC)&.first
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [ Regexp ] The pattern related to the target url, also matches escaped /, such as
|
|
62
|
+
# in JSON JS data: http:\/\/t.com\/
|
|
63
|
+
def url_pattern
|
|
64
|
+
@url_pattern ||= Regexp.new(Regexp.escape(url).gsub(/https?/i, 'https?').gsub('/', '\\\\\?/'), Regexp::IGNORECASE)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @param [ String ] xpath
|
|
68
|
+
# @param [ Regexp ] pattern
|
|
69
|
+
# @param [ Typhoeus::Response, String ] page
|
|
70
|
+
#
|
|
71
|
+
# @return [ Array<Array<MatchData, Nokogiri::XML::Element>> ]
|
|
72
|
+
# @yield [ MatchData, Nokogiri::XML::Element ]
|
|
73
|
+
def xpath_pattern_from_page(xpath, pattern, page = nil)
|
|
74
|
+
page = WPScan::Browser.get(url(page)) unless page.is_a?(Typhoeus::Response)
|
|
75
|
+
matches = []
|
|
76
|
+
|
|
77
|
+
page.html.xpath(xpath).each do |node|
|
|
78
|
+
next unless node.text.strip =~ pattern
|
|
79
|
+
|
|
80
|
+
yield Regexp.last_match, node if block_given?
|
|
81
|
+
|
|
82
|
+
matches << [Regexp.last_match, node]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
matches
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @param [ Regexp ] pattern
|
|
89
|
+
# @param [ Typhoeus::Response, String ] page
|
|
90
|
+
#
|
|
91
|
+
# @return [ Array<Array<MatchData, Nokogiri::XML::Comment>> ]
|
|
92
|
+
# @yield [ MatchData, Nokogiri::XML::Comment ]
|
|
93
|
+
def comments_from_page(pattern, page = nil)
|
|
94
|
+
xpath_pattern_from_page('//comment()', pattern, page) do |match, node|
|
|
95
|
+
yield match, node if block_given?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @param [ Regexp ] pattern
|
|
100
|
+
# @param [ Typhoeus::Response, String ] page
|
|
101
|
+
#
|
|
102
|
+
# @return [ Array<Array<MatchData, Nokogiri::XML::Element>> ]
|
|
103
|
+
# @yield [ MatchData, Nokogiri::XML::Element ]
|
|
104
|
+
def javascripts_from_page(pattern, page = nil)
|
|
105
|
+
xpath_pattern_from_page('//script', pattern, page) do |match, node|
|
|
106
|
+
yield match, node if block_given?
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @param [ Typhoeus::Response, String ] page
|
|
111
|
+
# @param [ String ] xpath
|
|
112
|
+
#
|
|
113
|
+
# @yield [ Addressable::URI, Nokogiri::XML::Element ] The url and its associated tag
|
|
114
|
+
#
|
|
115
|
+
# @return [ Array<Addressable::URI> ] The absolute URIs detected in the response's body from the HTML tags
|
|
116
|
+
#
|
|
117
|
+
# @note It is highly recommended to use the xpath parameter to focus on the uris needed, as this method can be quite
|
|
118
|
+
# time consuming when there are a lof of uris to check
|
|
119
|
+
def uris_from_page(page = nil, xpath = '//@href|//@src|//@data-src')
|
|
120
|
+
page = WPScan::Browser.get(url(page)) unless page.is_a?(Typhoeus::Response)
|
|
121
|
+
found = []
|
|
122
|
+
|
|
123
|
+
page.html.xpath(xpath).each do |node|
|
|
124
|
+
attr_value = node.text.to_s
|
|
125
|
+
|
|
126
|
+
next unless attr_value && !attr_value.empty?
|
|
127
|
+
|
|
128
|
+
node_uri = begin
|
|
129
|
+
uri.join(attr_value.strip)
|
|
130
|
+
rescue StandardError
|
|
131
|
+
# Skip potential malformed URLs etc.
|
|
132
|
+
next
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
next unless node_uri.host
|
|
136
|
+
|
|
137
|
+
yield node_uri, node.parent if block_given? && !found.include?(node_uri)
|
|
138
|
+
|
|
139
|
+
found << node_uri
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
found.uniq
|
|
36
143
|
end
|
|
37
144
|
|
|
38
145
|
# @param [ Hash ] opts
|
|
@@ -54,17 +161,21 @@ module WPScan
|
|
|
54
161
|
end
|
|
55
162
|
|
|
56
163
|
# @param [ Hash ] opts
|
|
164
|
+
# @yield [ Plugin ] Optional block called as each plugin is first
|
|
165
|
+
# discovered (used to stream findings).
|
|
57
166
|
#
|
|
58
167
|
# @return [ Array<Plugin> ]
|
|
59
|
-
def plugins(opts = {})
|
|
60
|
-
@plugins ||= Finders::Plugins::Base.find(self, opts)
|
|
168
|
+
def plugins(opts = {}, &)
|
|
169
|
+
@plugins ||= Finders::Plugins::Base.find(self, opts, &)
|
|
61
170
|
end
|
|
62
171
|
|
|
63
172
|
# @param [ Hash ] opts
|
|
173
|
+
# @yield [ Theme ] Optional block called as each theme is first
|
|
174
|
+
# discovered (used to stream findings).
|
|
64
175
|
#
|
|
65
176
|
# @return [ Array<Theme> ]
|
|
66
|
-
def themes(opts = {})
|
|
67
|
-
@themes ||= Finders::Themes::Base.find(self, opts)
|
|
177
|
+
def themes(opts = {}, &)
|
|
178
|
+
@themes ||= Finders::Themes::Base.find(self, opts, &)
|
|
68
179
|
end
|
|
69
180
|
|
|
70
181
|
# @param [ Hash ] opts
|
|
@@ -88,6 +199,13 @@ module WPScan
|
|
|
88
199
|
@db_exports ||= Finders::DbExports::Base.find(self, opts)
|
|
89
200
|
end
|
|
90
201
|
|
|
202
|
+
# @param [ Hash ] opts
|
|
203
|
+
#
|
|
204
|
+
# @return [ Array<BackupFolder> ]
|
|
205
|
+
def backup_folders(opts = {})
|
|
206
|
+
@backup_folders ||= Finders::BackupFolders::Base.find(self, opts)
|
|
207
|
+
end
|
|
208
|
+
|
|
91
209
|
# @param [ Hash ] opts
|
|
92
210
|
#
|
|
93
211
|
# @return [ Array<Media> ]
|
|
@@ -96,10 +214,12 @@ module WPScan
|
|
|
96
214
|
end
|
|
97
215
|
|
|
98
216
|
# @param [ Hash ] opts
|
|
217
|
+
# @yield [ User ] Optional block called as each user is first
|
|
218
|
+
# discovered (used to stream findings).
|
|
99
219
|
#
|
|
100
220
|
# @return [ Array<User> ]
|
|
101
|
-
def users(opts = {})
|
|
102
|
-
@users ||= Finders::Users::Base.find(self, opts)
|
|
221
|
+
def users(opts = {}, &)
|
|
222
|
+
@users ||= Finders::Users::Base.find(self, opts, &)
|
|
103
223
|
end
|
|
104
224
|
end
|
|
105
225
|
end
|
|
@@ -3,10 +3,33 @@
|
|
|
3
3
|
module Typhoeus
|
|
4
4
|
# Custom Response class
|
|
5
5
|
class Response
|
|
6
|
-
# @
|
|
6
|
+
# @return [ Nokogiri::XML ] The response's body parsed by Nokogiri::HTML
|
|
7
|
+
def html
|
|
8
|
+
@html ||= Nokogiri::HTML(body.encode('UTF-8', invalid: :replace, undef: :replace))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [ Nokogiri::XML ] The response's body parsed by Nokogiri::XML
|
|
12
|
+
def xml
|
|
13
|
+
@xml ||= Nokogiri::XML(body.encode('UTF-8', invalid: :replace, undef: :replace))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Override of the original to ensure an integer is returned
|
|
17
|
+
# @return [ Integer ]
|
|
18
|
+
def request_size
|
|
19
|
+
super || 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [ Integer ]
|
|
23
|
+
def size
|
|
24
|
+
(body.nil? ? 0 : body.size) + (response_headers.nil? ? 0 : response_headers.size)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @note Ignores requests done to the /status endpoint of the WPScan Vuln API.
|
|
7
28
|
#
|
|
8
29
|
# @return [ Boolean ]
|
|
9
30
|
def from_vuln_api?
|
|
31
|
+
return false unless effective_url
|
|
32
|
+
|
|
10
33
|
effective_url.start_with?(WPScan::DB::VulnApi.uri.to_s) &&
|
|
11
34
|
!effective_url.start_with?(WPScan::DB::VulnApi.uri.join('status').to_s)
|
|
12
35
|
end
|
data/lib/wpscan/version.rb
CHANGED
data/lib/wpscan/vulnerability.rb
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module WPScan
|
|
4
|
-
#
|
|
5
|
-
class Vulnerability
|
|
4
|
+
# Vulnerability model.
|
|
5
|
+
class Vulnerability
|
|
6
6
|
include References
|
|
7
7
|
|
|
8
|
+
attr_reader :title, :type, :fixed_in, :introduced_in, :cvss, :poc, :uuid
|
|
9
|
+
|
|
10
|
+
# @param [ String ] title
|
|
11
|
+
# @param [ Hash ] references
|
|
12
|
+
# @option references [ Array<String>, String ] :cve
|
|
13
|
+
# @option references [ Array<String>, String ] :secunia
|
|
14
|
+
# @option references [ Array<String>, String ] :osvdb
|
|
15
|
+
# @option references [ Array<String>, String ] :exploitdb
|
|
16
|
+
# @option references [ Array<String> ] :url URL(s) to related advisories etc
|
|
17
|
+
# @option references [ Array<String>, String ] :metasploit The related metasploit module(s)
|
|
18
|
+
# @option references [ Array<String> ] :youtube
|
|
19
|
+
# @param [ String ] type
|
|
20
|
+
# @param [ String ] fixed_in
|
|
21
|
+
# @param [ String ] introduced_in
|
|
22
|
+
# @param [ HashSymbol ] cvss
|
|
23
|
+
# @option cvss [ String ] :score
|
|
24
|
+
# @option cvss [ String ] :vector
|
|
25
|
+
# rubocop:disable Metrics/ParameterLists
|
|
26
|
+
def initialize(title, references: {}, type: nil, fixed_in: nil, introduced_in: nil, cvss: nil, poc: nil, uuid: nil)
|
|
27
|
+
# rubocop:enable Metrics/ParameterLists
|
|
28
|
+
@title = title
|
|
29
|
+
@type = type
|
|
30
|
+
@fixed_in = fixed_in
|
|
31
|
+
@introduced_in = introduced_in
|
|
32
|
+
@cvss = { score: cvss[:score], vector: cvss[:vector] } if cvss
|
|
33
|
+
@poc = poc
|
|
34
|
+
@uuid = uuid
|
|
35
|
+
|
|
36
|
+
self.references = references
|
|
37
|
+
end
|
|
38
|
+
|
|
8
39
|
# @param [ Hash ] json_data
|
|
9
40
|
# @return [ Vulnerability ]
|
|
10
41
|
def self.load_from_json(json_data)
|
|
@@ -22,8 +53,23 @@ module WPScan
|
|
|
22
53
|
type: json_data['vuln_type'],
|
|
23
54
|
fixed_in: json_data['fixed_in'],
|
|
24
55
|
introduced_in: json_data['introduced_in'],
|
|
25
|
-
cvss: json_data['cvss']&.symbolize_keys
|
|
56
|
+
cvss: json_data['cvss']&.symbolize_keys,
|
|
57
|
+
poc: json_data['poc'],
|
|
58
|
+
uuid: json_data['id'].to_s # The 'id' field IS the UUID in WPScan API
|
|
26
59
|
)
|
|
27
60
|
end
|
|
61
|
+
|
|
62
|
+
# @param [ Vulnerability ] other
|
|
63
|
+
#
|
|
64
|
+
# @return [ Boolean ]
|
|
65
|
+
def ==(other)
|
|
66
|
+
title == other.title &&
|
|
67
|
+
type == other.type &&
|
|
68
|
+
references == other.references &&
|
|
69
|
+
fixed_in == other.fixed_in &&
|
|
70
|
+
cvss == other.cvss &&
|
|
71
|
+
poc == other.poc &&
|
|
72
|
+
uuid == other.uuid
|
|
73
|
+
end
|
|
28
74
|
end
|
|
29
75
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
# Filter for excluding specific vulnerabilities by UUID
|
|
5
|
+
class VulnerabilityFilter
|
|
6
|
+
attr_reader :excluded_uuids, :excluded_count
|
|
7
|
+
|
|
8
|
+
# @param [ Array<String>, String, nil ] uuids UUID identifiers to exclude
|
|
9
|
+
def initialize(uuids = nil)
|
|
10
|
+
@excluded_uuids = normalize_uuids(uuids)
|
|
11
|
+
@excluded_count = 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Filter vulnerabilities, removing those with excluded UUIDs
|
|
15
|
+
#
|
|
16
|
+
# @param [ Array<Vulnerability> ] vulnerabilities
|
|
17
|
+
# @return [ Array<Vulnerability> ] Filtered vulnerabilities
|
|
18
|
+
def filter(vulnerabilities)
|
|
19
|
+
return vulnerabilities if excluded_uuids.empty?
|
|
20
|
+
|
|
21
|
+
vulnerabilities.reject do |vuln|
|
|
22
|
+
should_exclude?(vuln).tap do |excluded|
|
|
23
|
+
@excluded_count += 1 if excluded
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Check if a vulnerability should be excluded
|
|
29
|
+
#
|
|
30
|
+
# @param [ Vulnerability ] vulnerability
|
|
31
|
+
# @return [ Boolean ]
|
|
32
|
+
def should_exclude?(vulnerability)
|
|
33
|
+
return false if excluded_uuids.empty?
|
|
34
|
+
return false unless vulnerability.uuid
|
|
35
|
+
|
|
36
|
+
# Check UUID match (case-insensitive)
|
|
37
|
+
excluded_uuids.include?(vulnerability.uuid.downcase)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Reset the excluded count
|
|
41
|
+
def reset_count!
|
|
42
|
+
@excluded_count = 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if any UUIDs are being excluded
|
|
46
|
+
#
|
|
47
|
+
# @return [ Boolean ]
|
|
48
|
+
def excluding?
|
|
49
|
+
!excluded_uuids.empty?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Normalize UUIDs
|
|
55
|
+
#
|
|
56
|
+
# @param [ Array<String>, String, nil ] uuids
|
|
57
|
+
# @return [ Array<String> ] Normalized UUIDs
|
|
58
|
+
def normalize_uuids(uuids)
|
|
59
|
+
return [] if uuids.nil?
|
|
60
|
+
|
|
61
|
+
# Convert string to array if needed
|
|
62
|
+
uuids = uuids.is_a?(String) ? uuids.split(',') : Array(uuids)
|
|
63
|
+
|
|
64
|
+
# Normalize each UUID (trim whitespace, lowercase, remove duplicates)
|
|
65
|
+
uuids.map { |u| u.to_s.strip.downcase }.reject(&:empty?).uniq
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/wpscan/vulnerable.rb
CHANGED
|
@@ -4,9 +4,21 @@ module WPScan
|
|
|
4
4
|
# Module to include in vulnerable WP item such as WpVersion.
|
|
5
5
|
# the vulnerabilities method should be implemented
|
|
6
6
|
module Vulnerable
|
|
7
|
+
# @return [ VulnerabilityFilter, nil ]
|
|
8
|
+
def vulnerability_filter
|
|
9
|
+
@vulnerability_filter ||= VulnerabilityFilter.new(ParsedCli.exclude_vulns) if ParsedCli.exclude_vulns
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @return [ Array<Vulnerability> ] Filtered vulnerabilities
|
|
13
|
+
def filtered_vulnerabilities
|
|
14
|
+
return vulnerabilities unless vulnerability_filter
|
|
15
|
+
|
|
16
|
+
vulnerability_filter.filter(vulnerabilities)
|
|
17
|
+
end
|
|
18
|
+
|
|
7
19
|
# @return [ Boolean ]
|
|
8
20
|
def vulnerable?
|
|
9
|
-
!
|
|
21
|
+
!filtered_vulnerabilities.empty?
|
|
10
22
|
end
|
|
11
23
|
end
|
|
12
24
|
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
# WebSite Implementation
|
|
5
|
+
class WebSite
|
|
6
|
+
attr_reader :uri, :opts
|
|
7
|
+
|
|
8
|
+
# @param [ String ] site_url
|
|
9
|
+
# @param [ Hash ] opts
|
|
10
|
+
def initialize(site_url, opts = {})
|
|
11
|
+
self.url = site_url
|
|
12
|
+
@opts = opts
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def url=(site_url)
|
|
16
|
+
new_url = site_url.dup
|
|
17
|
+
|
|
18
|
+
# Add a trailing slash to the URL
|
|
19
|
+
new_url << '/' if new_url[-1, 1] != '/'
|
|
20
|
+
|
|
21
|
+
# Use the validator to ensure the URL has a correct format
|
|
22
|
+
OptParseValidator::OptURL.new([]).validate(new_url)
|
|
23
|
+
|
|
24
|
+
@uri = Addressable::URI.parse(new_url).normalize
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param [ String ] path Optional path to merge with the uri
|
|
28
|
+
#
|
|
29
|
+
# @return [ String ]
|
|
30
|
+
def url(path = nil)
|
|
31
|
+
return @uri.to_s unless path
|
|
32
|
+
|
|
33
|
+
@uri.join(Addressable::URI.encode(path).gsub('#', '%23')).to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [ String ] The IP address of the target
|
|
37
|
+
def ip
|
|
38
|
+
@ip ||= IPSocket.getaddress(uri.host)
|
|
39
|
+
rescue SocketError
|
|
40
|
+
'Unknown'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
attr_writer :homepage_res
|
|
44
|
+
|
|
45
|
+
# @return [ Typhoeus::Response ]
|
|
46
|
+
#
|
|
47
|
+
# As webmock does not support redirects mocking, coverage is ignored
|
|
48
|
+
# :nocov:
|
|
49
|
+
def homepage_res
|
|
50
|
+
@homepage_res ||= WPScan::Browser.get_and_follow_location(url)
|
|
51
|
+
end
|
|
52
|
+
# :nocov:
|
|
53
|
+
|
|
54
|
+
# @return [ String ]
|
|
55
|
+
def homepage_url
|
|
56
|
+
@homepage_url ||= homepage_res.effective_url
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Discards the cached homepage response and URL so the next access refetches.
|
|
60
|
+
# Used after mutating cookies/auth state mid-scan.
|
|
61
|
+
def reset_homepage_cache!
|
|
62
|
+
@homepage_res = nil
|
|
63
|
+
@homepage_url = nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [ Typhoeus::Response ]
|
|
67
|
+
def error_404_res
|
|
68
|
+
@error_404_res ||= WPScan::Browser.get_and_follow_location(error_404_url)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [ String ] The URL of an unlikely existant page
|
|
72
|
+
def error_404_url
|
|
73
|
+
@error_404_url ||= uri.join("#{Digest::MD5.hexdigest(rand(999_999).to_s)[0..6]}.html").to_s
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Checks if the remote website is up.
|
|
77
|
+
#
|
|
78
|
+
# @param [ String ] path
|
|
79
|
+
#
|
|
80
|
+
# @return [ Boolean ]
|
|
81
|
+
def online?(path = nil)
|
|
82
|
+
WPScan::Browser.get(url(path)).code.nonzero? ? true : false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param [ String ] path
|
|
86
|
+
#
|
|
87
|
+
# @return [ Boolean ]
|
|
88
|
+
def http_auth?(path = nil)
|
|
89
|
+
WPScan::Browser.get(url(path)).code == 401
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @param [ String ] path
|
|
93
|
+
#
|
|
94
|
+
# @return [ Boolean ]
|
|
95
|
+
def access_forbidden?(path = nil)
|
|
96
|
+
WPScan::Browser.get(url(path)).code == 403
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @param [ String ] path
|
|
100
|
+
#
|
|
101
|
+
# @return [ Boolean ]
|
|
102
|
+
def proxy_auth?(path = nil)
|
|
103
|
+
WPScan::Browser.get(url(path)).code == 407
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @param [ String ] url
|
|
107
|
+
#
|
|
108
|
+
# @return [ String ] The redirection url or nil
|
|
109
|
+
#
|
|
110
|
+
# As webmock does not support redirects mocking, coverage is ignored
|
|
111
|
+
# :nocov:
|
|
112
|
+
def redirection(url = nil)
|
|
113
|
+
url ||= @uri.to_s
|
|
114
|
+
|
|
115
|
+
return unless [301, 302].include?(WPScan::Browser.get(url).code)
|
|
116
|
+
|
|
117
|
+
res = WPScan::Browser.get(url, followlocation: true, maxredirs: 10)
|
|
118
|
+
|
|
119
|
+
res.effective_url == url ? nil : res.effective_url
|
|
120
|
+
end
|
|
121
|
+
# :nocov:
|
|
122
|
+
|
|
123
|
+
# @return [ Hash ] The Typhoeus params to use to perform head requests
|
|
124
|
+
def head_or_get_params
|
|
125
|
+
@head_or_get_params ||= if [0, 405, 501].include?(WPScan::Browser.head(homepage_url).code)
|
|
126
|
+
{ method: :get, maxfilesize: 1 }
|
|
127
|
+
else
|
|
128
|
+
{ method: :head }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Perform a HEAD request to the path provided, then if its response code
|
|
133
|
+
# is in the array of codes given, a GET is done and the response returned. Otherwise the
|
|
134
|
+
# HEAD response is returned.
|
|
135
|
+
#
|
|
136
|
+
# @param [ String ] path
|
|
137
|
+
# @param [ Array<String> ] codes
|
|
138
|
+
# @param [ Hash ] params The requests params
|
|
139
|
+
# @option params [ Hash ] :head Request params for the HEAD
|
|
140
|
+
# @option params [ hash ] :get Request params for the GET
|
|
141
|
+
#
|
|
142
|
+
# @return [ Typhoeus::Response ]
|
|
143
|
+
def head_and_get(path, codes = [200], params = {})
|
|
144
|
+
url_to_get = url(path)
|
|
145
|
+
head_params = (params[:head] || {}).merge(head_or_get_params)
|
|
146
|
+
|
|
147
|
+
head_res = WPScan::Browser.forge_request(url_to_get, head_params).run
|
|
148
|
+
|
|
149
|
+
codes.include?(head_res.code) ? WPScan::Browser.get(url_to_get, params[:get] || {}) : head_res
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|