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
data/app/controllers/core.rb
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'core/cli_options'
|
|
4
|
+
require 'socket'
|
|
5
|
+
|
|
3
6
|
module WPScan
|
|
4
7
|
module Controller
|
|
5
|
-
#
|
|
6
|
-
class Core <
|
|
8
|
+
# Core Controller (WordPress-aware).
|
|
9
|
+
class Core < Base
|
|
7
10
|
# @return [ Array<OptParseValidator::Opt> ]
|
|
8
11
|
def cli_options
|
|
9
12
|
[OptURL.new(['--url URL', 'The URL of the blog to scan'],
|
|
10
13
|
required_unless: %i[update help hh version], default_protocol: 'http')] +
|
|
11
|
-
|
|
14
|
+
base_cli_options.drop(2) + # drop the base --url and --force
|
|
12
15
|
[
|
|
13
16
|
OptChoice.new(['--server SERVER', 'Force the supplied server module to be loaded'],
|
|
14
17
|
choices: %w[apache iis nginx],
|
|
@@ -19,6 +22,156 @@ module WPScan
|
|
|
19
22
|
]
|
|
20
23
|
end
|
|
21
24
|
|
|
25
|
+
def setup_cache
|
|
26
|
+
return unless WPScan::ParsedCli.cache_dir
|
|
27
|
+
|
|
28
|
+
storage_path = File.join(WPScan::ParsedCli.cache_dir, Digest::MD5.hexdigest(target.url))
|
|
29
|
+
|
|
30
|
+
Typhoeus::Config.cache = Cache::Typhoeus.new(storage_path)
|
|
31
|
+
Typhoeus::Config.cache.clean if WPScan::ParsedCli.clear_cache
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def before_scan
|
|
35
|
+
@last_update = local_db.last_update
|
|
36
|
+
@saml_authenticated = false
|
|
37
|
+
|
|
38
|
+
maybe_output_banner_help_and_version
|
|
39
|
+
|
|
40
|
+
update_db if update_db_required?
|
|
41
|
+
setup_cache
|
|
42
|
+
check_target_availability
|
|
43
|
+
load_server_module
|
|
44
|
+
check_wordpress_state
|
|
45
|
+
rescue Error::NotWordPress => e
|
|
46
|
+
target.maybe_add_cookies
|
|
47
|
+
raise e unless target.wordpress?(ParsedCli.detection_mode)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def maybe_output_banner_help_and_version
|
|
51
|
+
output('banner') if WPScan::ParsedCli.banner
|
|
52
|
+
output('help', help: option_parser.simple_help, simple: true) if WPScan::ParsedCli.help
|
|
53
|
+
output('help', help: option_parser.full_help, simple: false) if WPScan::ParsedCli.hh
|
|
54
|
+
output('version') if WPScan::ParsedCli.version
|
|
55
|
+
|
|
56
|
+
exit(WPScan::ExitCode::OK) if WPScan::ParsedCli.help || WPScan::ParsedCli.hh || WPScan::ParsedCli.version
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Checks that the target is accessible, raises related errors otherwise.
|
|
60
|
+
#
|
|
61
|
+
# @return [ Void ]
|
|
62
|
+
def check_target_availability
|
|
63
|
+
res = WPScan::Browser.get(target.url)
|
|
64
|
+
|
|
65
|
+
case res.code
|
|
66
|
+
when 0
|
|
67
|
+
raise Error::TargetDown, res
|
|
68
|
+
when 401
|
|
69
|
+
raise Error::HTTPAuthRequired
|
|
70
|
+
when 403
|
|
71
|
+
raise Error::AccessForbidden, WPScan::ParsedCli.random_user_agent unless WPScan::ParsedCli.force
|
|
72
|
+
when 407
|
|
73
|
+
raise Error::ProxyAuthRequired
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
handle_redirection(res)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Checks whether the response or its redirect chain contains a SAMLRequest,
|
|
80
|
+
# indicating that the target requires SAML authentication.
|
|
81
|
+
#
|
|
82
|
+
# @param [ Addressable::URI ] effective_uri Final URL after following redirects
|
|
83
|
+
# @param [ Typhoeus::Response ] homepage_res Response whose redirect chain to inspect
|
|
84
|
+
#
|
|
85
|
+
# @return [ Boolean ]
|
|
86
|
+
def saml_request?(effective_uri, homepage_res = nil)
|
|
87
|
+
return false unless effective_uri
|
|
88
|
+
|
|
89
|
+
return true if effective_uri.to_s.match?(/[?&]SAMLRequest/i)
|
|
90
|
+
|
|
91
|
+
# SAML flows often bounce through intermediate pages before the IdP;
|
|
92
|
+
# walk the redirect chain to catch a SAMLRequest in any Location header.
|
|
93
|
+
!!homepage_res&.redirections&.any? do |redirect_response|
|
|
94
|
+
redirect_response.headers['Location']&.match?(/SAMLRequest/i)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Drives an interactive SAML login via a headless browser, injects the
|
|
99
|
+
# resulting session cookies into the shared Browser, and clears the target's
|
|
100
|
+
# cached homepage so the rest of the scan runs against the authenticated session.
|
|
101
|
+
#
|
|
102
|
+
# @param [ Addressable::URI ] effective_uri URL that triggered the SAML redirect
|
|
103
|
+
#
|
|
104
|
+
# @return [ Void ]
|
|
105
|
+
def handle_saml_authentication(effective_uri)
|
|
106
|
+
raise Error::SAMLAuthenticationFailed if WPScan::ParsedCli.cookie_string && !WPScan::ParsedCli.expect_saml
|
|
107
|
+
raise Error::SAMLAuthenticationRequired unless WPScan::ParsedCli.expect_saml
|
|
108
|
+
|
|
109
|
+
new_cookies = BrowserAuthenticator.authenticate(effective_uri.to_s)
|
|
110
|
+
|
|
111
|
+
browser = WPScan::Browser.instance
|
|
112
|
+
browser.cookie_string = [browser.cookie_string, new_cookies].compact.reject(&:empty?).join('; ')
|
|
113
|
+
|
|
114
|
+
# Discard the pre-auth homepage so subsequent finders refetch with the new cookies.
|
|
115
|
+
target.reset_homepage_cache!
|
|
116
|
+
|
|
117
|
+
@saml_authenticated = true
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Checks for redirects; an out-of-scope redirect raises Error::HTTPRedirect.
|
|
121
|
+
#
|
|
122
|
+
# @param [ Typhoeus::Response ] res
|
|
123
|
+
def handle_redirection(res)
|
|
124
|
+
effective_url = target.homepage_res.effective_url # get and follow location of target.url
|
|
125
|
+
effective_uri = Addressable::URI.parse(effective_url)
|
|
126
|
+
is_saml = saml_request?(effective_uri, target.homepage_res)
|
|
127
|
+
|
|
128
|
+
if WPScan::ParsedCli.expect_saml && !is_saml && !@saml_authenticated
|
|
129
|
+
puts 'SAML authentication was expected but not required.'
|
|
130
|
+
puts # New line to serve as buffer before the scan results start
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if is_saml
|
|
134
|
+
raise Error::SAMLAuthenticationFailed if @saml_authenticated
|
|
135
|
+
|
|
136
|
+
handle_saml_authentication(effective_uri)
|
|
137
|
+
return check_target_availability
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
handle_scheme_redirect(effective_url, effective_uri)
|
|
141
|
+
handle_follow_redirect(effective_url, effective_uri)
|
|
142
|
+
|
|
143
|
+
return if target.in_scope?(effective_url)
|
|
144
|
+
|
|
145
|
+
raise Error::HTTPRedirect, effective_url unless WPScan::ParsedCli.ignore_main_redirect
|
|
146
|
+
|
|
147
|
+
# Sets homepage_res back to unfollowed response when ignore_main_redirect is used
|
|
148
|
+
target.homepage_res = res
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Handles scheme-only redirects (http => https or vice versa)
|
|
152
|
+
#
|
|
153
|
+
# @param [ String ] effective_url
|
|
154
|
+
# @param [ Addressable::URI ] effective_uri
|
|
155
|
+
def handle_scheme_redirect(effective_url, effective_uri)
|
|
156
|
+
# http://a.com => https://a.com (or the opposite)
|
|
157
|
+
if !WPScan::ParsedCli.ignore_main_redirect && target.uri.domain == effective_uri.domain &&
|
|
158
|
+
target.uri.path == effective_uri.path && target.uri.scheme != effective_uri.scheme
|
|
159
|
+
|
|
160
|
+
target.url = effective_url
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Handles --follow-redirect option
|
|
165
|
+
#
|
|
166
|
+
# @param [ String ] effective_url
|
|
167
|
+
# @param [ Addressable::URI ] effective_uri
|
|
168
|
+
def handle_follow_redirect(effective_url, effective_uri)
|
|
169
|
+
return unless WPScan::ParsedCli.follow_redirect && target.url != effective_url
|
|
170
|
+
|
|
171
|
+
target.url = effective_url
|
|
172
|
+
target.scope << effective_uri.host
|
|
173
|
+
end
|
|
174
|
+
|
|
22
175
|
# @return [ DB::Updater ]
|
|
23
176
|
def local_db
|
|
24
177
|
@local_db ||= DB::Updater.new(DB_DIR)
|
|
@@ -38,34 +191,29 @@ module WPScan
|
|
|
38
191
|
|
|
39
192
|
output('@notice', msg: 'It seems like you have not updated the database for some time.')
|
|
40
193
|
print '[?] Do you want to update now? [Y]es [N]o, default: [N]'
|
|
194
|
+
$stdout.flush
|
|
195
|
+
|
|
196
|
+
response = $stdin.gets.to_s.strip
|
|
41
197
|
|
|
42
|
-
|
|
198
|
+
!!/^y/i.match?(response)
|
|
43
199
|
end
|
|
44
200
|
|
|
45
201
|
def update_db
|
|
202
|
+
@updating_db = true
|
|
46
203
|
output('db_update_started')
|
|
47
204
|
output('db_update_finished', updated: local_db.update, verbose: ParsedCli.verbose)
|
|
205
|
+
@updating_db = false
|
|
48
206
|
|
|
49
207
|
exit(0) unless ParsedCli.url
|
|
50
208
|
end
|
|
51
209
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
maybe_output_banner_help_and_version # From CMSScanner
|
|
56
|
-
|
|
57
|
-
update_db if update_db_required?
|
|
58
|
-
setup_cache
|
|
59
|
-
check_target_availability
|
|
60
|
-
load_server_module
|
|
61
|
-
check_wordpress_state
|
|
62
|
-
rescue Error::NotWordPress => e
|
|
63
|
-
target.maybe_add_cookies
|
|
64
|
-
raise e unless target.wordpress?(ParsedCli.detection_mode)
|
|
210
|
+
# @return [ Boolean ] Whether the DB update is currently in progress
|
|
211
|
+
def updating_db?
|
|
212
|
+
@updating_db
|
|
65
213
|
end
|
|
66
214
|
|
|
67
|
-
# Raises errors if the target is hosted on wordpress.com or is not running WordPress
|
|
68
|
-
# Also
|
|
215
|
+
# Raises errors if the target is hosted on wordpress.com or is not running WordPress.
|
|
216
|
+
# Also checks if the homepage_url is still the install URL.
|
|
69
217
|
def check_wordpress_state
|
|
70
218
|
raise Error::WordPressHosted if target.wordpress_hosted?
|
|
71
219
|
|
|
@@ -79,15 +227,13 @@ module WPScan
|
|
|
79
227
|
raise Error::NotWordPress unless target.wordpress?(ParsedCli.detection_mode) || ParsedCli.force
|
|
80
228
|
end
|
|
81
229
|
|
|
82
|
-
# Loads the related server module
|
|
83
|
-
#
|
|
84
|
-
# to check if directory listing is enabled etc
|
|
230
|
+
# Loads the related server module into the target and includes it on WpItem
|
|
231
|
+
# (needed to check if directory listing is enabled etc.).
|
|
85
232
|
#
|
|
86
233
|
# @return [ Symbol ] The server module loaded
|
|
87
234
|
def load_server_module
|
|
88
|
-
server = target.server || :Apache #
|
|
235
|
+
server = target.server || :Apache # auto-detect
|
|
89
236
|
|
|
90
|
-
# Force a specific server module to be loaded if supplied
|
|
91
237
|
case ParsedCli.server
|
|
92
238
|
when :apache
|
|
93
239
|
server = :Apache
|
|
@@ -97,13 +243,42 @@ module WPScan
|
|
|
97
243
|
server = :Nginx
|
|
98
244
|
end
|
|
99
245
|
|
|
100
|
-
mod =
|
|
246
|
+
mod = WPScan::Target::Server.const_get(server)
|
|
101
247
|
|
|
102
248
|
target.extend mod
|
|
103
249
|
Model::WpItem.include mod
|
|
104
250
|
|
|
105
251
|
server
|
|
106
252
|
end
|
|
253
|
+
|
|
254
|
+
def run
|
|
255
|
+
@start_time = Time.now
|
|
256
|
+
@start_memory = WPScan.start_memory
|
|
257
|
+
|
|
258
|
+
output('started',
|
|
259
|
+
url: target.url,
|
|
260
|
+
ip: target.ip,
|
|
261
|
+
effective_url: target.homepage_url,
|
|
262
|
+
command_line: WPScan.command_line,
|
|
263
|
+
hostname: Socket.gethostname)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def after_scan
|
|
267
|
+
@stop_time = Time.now
|
|
268
|
+
@elapsed = @stop_time - @start_time
|
|
269
|
+
@used_memory = GetProcessMem.new.bytes - @start_memory
|
|
270
|
+
|
|
271
|
+
warnings = WPScan.error_warning_messages
|
|
272
|
+
|
|
273
|
+
output('finished',
|
|
274
|
+
cached_requests: WPScan.cached_requests,
|
|
275
|
+
requests_done: WPScan.total_requests,
|
|
276
|
+
data_sent: WPScan.total_data_sent,
|
|
277
|
+
data_received: WPScan.total_data_received,
|
|
278
|
+
response_status_codes: WPScan.format_status_codes(WPScan.top_status_codes),
|
|
279
|
+
response_status_codes_warning: warnings.any?,
|
|
280
|
+
response_status_codes_warnings: warnings)
|
|
281
|
+
end
|
|
107
282
|
end
|
|
108
283
|
end
|
|
109
284
|
end
|
|
@@ -4,7 +4,7 @@ module WPScan
|
|
|
4
4
|
module Controller
|
|
5
5
|
# Controller to ensure that the wp-content and wp-plugins
|
|
6
6
|
# directories are found
|
|
7
|
-
class CustomDirectories <
|
|
7
|
+
class CustomDirectories < WPScan::Controller::Base
|
|
8
8
|
def cli_options
|
|
9
9
|
[
|
|
10
10
|
OptString.new(['--wp-content-dir DIR',
|
|
@@ -3,18 +3,19 @@
|
|
|
3
3
|
module WPScan
|
|
4
4
|
module Controller
|
|
5
5
|
# Enumeration CLI Options
|
|
6
|
-
class Enumeration <
|
|
6
|
+
class Enumeration < WPScan::Controller::Base
|
|
7
7
|
def cli_options
|
|
8
8
|
cli_enum_choices + cli_plugins_opts + cli_themes_opts +
|
|
9
9
|
cli_timthumbs_opts + cli_config_backups_opts + cli_db_exports_opts +
|
|
10
|
-
cli_medias_opts + cli_users_opts
|
|
10
|
+
cli_backup_folders_opts + cli_medias_opts + cli_users_opts
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# @return [ Array<OptParseValidator::OptBase> ]
|
|
14
14
|
def cli_enum_choices
|
|
15
15
|
[
|
|
16
16
|
OptMultiChoices.new(
|
|
17
|
-
['-e', '--enumerate [OPTS]', 'Enumeration Process'
|
|
17
|
+
['-e', '--enumerate [OPTS]', 'Enumeration Process',
|
|
18
|
+
'Note: --plugins-list overrides vp/ap/p; --themes-list overrides vt/at/t.'],
|
|
18
19
|
choices: {
|
|
19
20
|
vp: OptBoolean.new(['--vulnerable-plugins']),
|
|
20
21
|
ap: OptBoolean.new(['--all-plugins']),
|
|
@@ -25,15 +26,15 @@ module WPScan
|
|
|
25
26
|
tt: OptBoolean.new(['--timthumbs']),
|
|
26
27
|
cb: OptBoolean.new(['--config-backups']),
|
|
27
28
|
dbe: OptBoolean.new(['--db-exports']),
|
|
29
|
+
bf: OptBoolean.new(['--backup-folders']),
|
|
28
30
|
u: OptIntegerRange.new(['--users', 'User IDs range. e.g: u1-5'], value_if_empty: '1-10'),
|
|
29
31
|
m: OptIntegerRange.new(['--medias',
|
|
30
32
|
'Media IDs range. e.g m1-15',
|
|
31
33
|
'Note: Permalink setting must be set to "Plain" for those to be detected'],
|
|
32
34
|
value_if_empty: '1-100')
|
|
33
35
|
},
|
|
34
|
-
value_if_empty: 'vp,vt,tt,cb,dbe,u,m',
|
|
35
|
-
incompatible: [%i[vp ap p], %i[vt at t]]
|
|
36
|
-
default: { all_plugins: true, config_backups: true }
|
|
36
|
+
value_if_empty: 'vp,vt,tt,cb,dbe,bf,u,m',
|
|
37
|
+
incompatible: [%i[vp ap p], %i[vt at t]]
|
|
37
38
|
),
|
|
38
39
|
OptRegexp.new(
|
|
39
40
|
[
|
|
@@ -52,7 +53,7 @@ module WPScan
|
|
|
52
53
|
OptChoice.new(
|
|
53
54
|
['--plugins-detection MODE',
|
|
54
55
|
'Use the supplied mode to enumerate Plugins.'],
|
|
55
|
-
choices: %w[mixed passive aggressive], normalize: :to_sym
|
|
56
|
+
choices: %w[mixed passive aggressive], normalize: :to_sym
|
|
56
57
|
),
|
|
57
58
|
OptBoolean.new(
|
|
58
59
|
['--plugins-version-all',
|
|
@@ -63,7 +64,7 @@ module WPScan
|
|
|
63
64
|
OptChoice.new(
|
|
64
65
|
['--plugins-version-detection MODE',
|
|
65
66
|
'Use the supplied mode to check plugins\' versions.'],
|
|
66
|
-
choices: %w[mixed passive aggressive], normalize: :to_sym
|
|
67
|
+
choices: %w[mixed passive aggressive], normalize: :to_sym
|
|
67
68
|
),
|
|
68
69
|
OptInteger.new(
|
|
69
70
|
['--plugins-threshold THRESHOLD',
|
|
@@ -107,12 +108,7 @@ module WPScan
|
|
|
107
108
|
[
|
|
108
109
|
OptFilePath.new(
|
|
109
110
|
['--timthumbs-list FILE-PATH', 'List of timthumbs\' location to use'],
|
|
110
|
-
exists: true, default: DB_DIR.join('timthumbs-v3.txt')
|
|
111
|
-
),
|
|
112
|
-
OptChoice.new(
|
|
113
|
-
['--timthumbs-detection MODE',
|
|
114
|
-
'Use the supplied mode to enumerate Timthumbs, instead of the global (--detection-mode) mode.'],
|
|
115
|
-
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
|
|
111
|
+
exists: true, default: DB_DIR.join('timthumbs-v3.txt'), advanced: true
|
|
116
112
|
)
|
|
117
113
|
]
|
|
118
114
|
end
|
|
@@ -122,12 +118,7 @@ module WPScan
|
|
|
122
118
|
[
|
|
123
119
|
OptFilePath.new(
|
|
124
120
|
['--config-backups-list FILE-PATH', 'List of config backups\' filenames to use'],
|
|
125
|
-
exists: true, default: DB_DIR.join('config_backups.txt')
|
|
126
|
-
),
|
|
127
|
-
OptChoice.new(
|
|
128
|
-
['--config-backups-detection MODE',
|
|
129
|
-
'Use the supplied mode to enumerate Config Backups, instead of the global (--detection-mode) mode.'],
|
|
130
|
-
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
|
|
121
|
+
exists: true, default: DB_DIR.join('config_backups.txt'), advanced: true
|
|
131
122
|
)
|
|
132
123
|
]
|
|
133
124
|
end
|
|
@@ -137,27 +128,26 @@ module WPScan
|
|
|
137
128
|
[
|
|
138
129
|
OptFilePath.new(
|
|
139
130
|
['--db-exports-list FILE-PATH', 'List of DB exports\' paths to use'],
|
|
140
|
-
exists: true, default: DB_DIR.join('db_exports.txt')
|
|
141
|
-
),
|
|
142
|
-
OptChoice.new(
|
|
143
|
-
['--db-exports-detection MODE',
|
|
144
|
-
'Use the supplied mode to enumerate DB Exports, instead of the global (--detection-mode) mode.'],
|
|
145
|
-
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
|
|
131
|
+
exists: true, default: DB_DIR.join('db_exports.txt'), advanced: true
|
|
146
132
|
)
|
|
147
133
|
]
|
|
148
134
|
end
|
|
149
135
|
|
|
150
136
|
# @return [ Array<OptParseValidator::OptBase> ]
|
|
151
|
-
def
|
|
137
|
+
def cli_backup_folders_opts
|
|
152
138
|
[
|
|
153
|
-
|
|
154
|
-
['--
|
|
155
|
-
|
|
156
|
-
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
|
|
139
|
+
OptFilePath.new(
|
|
140
|
+
['--backup-folders-list FILE-PATH', 'List of backup folders to use'],
|
|
141
|
+
exists: true, default: DB_DIR.join('backup_folders.txt'), advanced: true
|
|
157
142
|
)
|
|
158
143
|
]
|
|
159
144
|
end
|
|
160
145
|
|
|
146
|
+
# @return [ Array<OptParseValidator::OptBase> ]
|
|
147
|
+
def cli_medias_opts
|
|
148
|
+
[]
|
|
149
|
+
end
|
|
150
|
+
|
|
161
151
|
# @return [ Array<OptParseValidator::OptBase> ]
|
|
162
152
|
def cli_users_opts
|
|
163
153
|
[
|