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
|
@@ -4,8 +4,8 @@ module WPScan
|
|
|
4
4
|
module Finders
|
|
5
5
|
module Themes
|
|
6
6
|
# Known Locations Themes Finder
|
|
7
|
-
class KnownLocations <
|
|
8
|
-
include
|
|
7
|
+
class KnownLocations < WPScan::Finders::Finder
|
|
8
|
+
include WPScan::Finders::Finder::Enumerator
|
|
9
9
|
|
|
10
10
|
# @return [ Array<Integer> ]
|
|
11
11
|
def valid_response_codes
|
|
@@ -14,22 +14,28 @@ module WPScan
|
|
|
14
14
|
|
|
15
15
|
# @param [ Hash ] opts
|
|
16
16
|
# @option opts [ String ] :list
|
|
17
|
+
# @option opts [ Findings ] :found Shared findings collection; see
|
|
18
|
+
# {Plugins::KnownLocations#aggressive} for the streaming rationale.
|
|
17
19
|
#
|
|
18
20
|
# @return [ Array<Theme> ]
|
|
19
21
|
def aggressive(opts = {})
|
|
20
|
-
|
|
22
|
+
shared = opts[:found]
|
|
23
|
+
local = shared ? nil : []
|
|
24
|
+
count = 0
|
|
21
25
|
|
|
22
26
|
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
|
|
23
27
|
finding_opts = opts.merge(found_by: found_by,
|
|
24
28
|
confidence: 80,
|
|
25
29
|
interesting_entries: ["#{res.effective_url}, status: #{res.code}"])
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
theme = Model::Theme.new(slug, target, finding_opts)
|
|
32
|
+
(shared || local) << theme
|
|
33
|
+
count += 1
|
|
28
34
|
|
|
29
|
-
raise Error::ThemesThresholdReached if opts[:threshold].positive? &&
|
|
35
|
+
raise Error::ThemesThresholdReached if opts[:threshold].positive? && count >= opts[:threshold]
|
|
30
36
|
end
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
local || []
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
# @param [ Hash ] opts
|
|
@@ -4,20 +4,16 @@ module WPScan
|
|
|
4
4
|
module Finders
|
|
5
5
|
module Themes
|
|
6
6
|
# URLs In Homepage Finder
|
|
7
|
-
class UrlsInHomepage <
|
|
7
|
+
class UrlsInHomepage < WPScan::Finders::Finder
|
|
8
8
|
include WpItems::UrlsInPage
|
|
9
9
|
|
|
10
10
|
# @param [ Hash ] opts
|
|
11
11
|
#
|
|
12
12
|
# @return [ Array<Theme> ]
|
|
13
13
|
def passive(opts = {})
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
(items_from_links('themes') + items_from_codes('themes')).uniq.sort.each do |slug|
|
|
17
|
-
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
|
|
14
|
+
(items_from_links('themes') + items_from_codes('themes')).uniq.sort.map do |slug|
|
|
15
|
+
Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
|
|
18
16
|
end
|
|
19
|
-
|
|
20
|
-
found
|
|
21
17
|
end
|
|
22
18
|
|
|
23
19
|
# @return [ Typhoeus::Response ]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
module Finders
|
|
5
|
+
module Themes
|
|
6
|
+
# Authenticated theme inventory via the WordPress REST API
|
|
7
|
+
# endpoint /wp-json/wp/v2/themes (WP >= 5.7).
|
|
8
|
+
#
|
|
9
|
+
# Requires admin credentials with the switch_themes capability.
|
|
10
|
+
class WpJsonApi < WPScan::Finders::Finder
|
|
11
|
+
FOUND_BY = 'WP REST API (Authenticated)'
|
|
12
|
+
|
|
13
|
+
# @param [ Hash ] opts
|
|
14
|
+
# @option opts [ String ] :userpwd "user:password" credentials for Basic Auth
|
|
15
|
+
#
|
|
16
|
+
# @return [ Array<Model::Theme> ]
|
|
17
|
+
def aggressive(opts = {})
|
|
18
|
+
response = Browser.get(api_url, userpwd: opts[:userpwd], headers: { 'Accept' => 'application/json' })
|
|
19
|
+
|
|
20
|
+
raise Error::WpAuthFailed.new(response.code, api_url) if [401, 403].include?(response.code)
|
|
21
|
+
raise Error::WpAuthEndpointUnavailable.new(response.code, api_url) unless response.code == 200
|
|
22
|
+
|
|
23
|
+
themes_from_response(response)
|
|
24
|
+
rescue JSON::ParserError, TypeError
|
|
25
|
+
[]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param [ Typhoeus::Response ] response
|
|
29
|
+
#
|
|
30
|
+
# @return [ Array<Model::Theme> ]
|
|
31
|
+
def themes_from_response(response)
|
|
32
|
+
json = JSON.parse(response.body)
|
|
33
|
+
return [] unless json.is_a?(Enumerable)
|
|
34
|
+
|
|
35
|
+
json.filter_map { |entry| build_theme(entry, response.effective_url) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [ String ] The REST API URL for the themes endpoint
|
|
39
|
+
def api_url
|
|
40
|
+
@api_url ||= target.url('wp-json/wp/v2/themes')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# @param [ Hash ] entry
|
|
46
|
+
# @param [ String ] effective_url
|
|
47
|
+
#
|
|
48
|
+
# @return [ Model::Theme, nil ]
|
|
49
|
+
def build_theme(entry, effective_url)
|
|
50
|
+
slug = entry['stylesheet']
|
|
51
|
+
return nil if slug.nil? || slug.to_s.empty?
|
|
52
|
+
|
|
53
|
+
theme = Model::Theme.new(
|
|
54
|
+
slug,
|
|
55
|
+
target,
|
|
56
|
+
confidence: 100,
|
|
57
|
+
found_by: FOUND_BY,
|
|
58
|
+
interesting_entries: [effective_url]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
version = entry['version']
|
|
62
|
+
theme.instance_variable_set(
|
|
63
|
+
:@version,
|
|
64
|
+
version && !version.to_s.empty? ? Model::Version.new(version, confidence: 100, found_by: FOUND_BY) : false
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
theme.instance_variable_set(:@wp_json_active, entry['status'] == 'active')
|
|
68
|
+
|
|
69
|
+
theme
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/app/finders/themes.rb
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
require_relative 'themes/urls_in_homepage'
|
|
4
4
|
require_relative 'themes/urls_in_404_page'
|
|
5
5
|
require_relative 'themes/known_locations'
|
|
6
|
+
require_relative 'themes/wp_json_api' # Authenticated, used by AuthenticatedInventory controller
|
|
6
7
|
|
|
7
8
|
module WPScan
|
|
8
9
|
module Finders
|
|
9
10
|
module Themes
|
|
10
11
|
# Themes Finder
|
|
11
12
|
class Base
|
|
12
|
-
include
|
|
13
|
+
include WPScan::Finders::SameTypeFinder
|
|
13
14
|
|
|
14
15
|
# @param [ WPScan::Target ] target
|
|
15
16
|
def initialize(target)
|
|
@@ -5,7 +5,7 @@ module WPScan
|
|
|
5
5
|
module TimthumbVersion
|
|
6
6
|
# Timthumb Version Finder from the body of a bad request
|
|
7
7
|
# See https://code.google.com/p/timthumb/source/browse/trunk/timthumb.php#435
|
|
8
|
-
class BadRequest <
|
|
8
|
+
class BadRequest < WPScan::Finders::Finder
|
|
9
9
|
# @return [ Version ]
|
|
10
10
|
def aggressive(_opts = {})
|
|
11
11
|
return unless Browser.get(target.url).body =~ /(TimThumb version\s*: ([^<]+))/
|
|
@@ -6,8 +6,8 @@ module WPScan
|
|
|
6
6
|
# Known Locations Timthumbs Finder
|
|
7
7
|
# Note: A vulnerable version, 2.8.13 can be found here:
|
|
8
8
|
# https://github.com/GabrielGil/TimThumb/blob/980c3d6a823477761570475e8b83d3e9fcd2d7ae/timthumb.php
|
|
9
|
-
class KnownLocations <
|
|
10
|
-
include
|
|
9
|
+
class KnownLocations < WPScan::Finders::Finder
|
|
10
|
+
include WPScan::Finders::Finder::Enumerator
|
|
11
11
|
|
|
12
12
|
# @return [ Array<Integer> ]
|
|
13
13
|
def valid_response_codes
|
|
@@ -37,8 +37,10 @@ module WPScan
|
|
|
37
37
|
def target_urls(opts = {})
|
|
38
38
|
urls = {}
|
|
39
39
|
|
|
40
|
-
File.open(opts[:list])
|
|
41
|
-
|
|
40
|
+
File.open(opts[:list]) do |f|
|
|
41
|
+
f.each_with_index do |path, index|
|
|
42
|
+
urls[target.url(path.chomp)] = index
|
|
43
|
+
end
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
# Add potential timthumbs located in the main theme
|
data/app/finders/timthumbs.rb
CHANGED
|
@@ -4,8 +4,8 @@ module WPScan
|
|
|
4
4
|
module Finders
|
|
5
5
|
module Users
|
|
6
6
|
# Author Id Brute Forcing
|
|
7
|
-
class AuthorIdBruteForcing <
|
|
8
|
-
include
|
|
7
|
+
class AuthorIdBruteForcing < WPScan::Finders::Finder
|
|
8
|
+
include WPScan::Finders::Finder::Enumerator
|
|
9
9
|
|
|
10
10
|
# @return [ Array<Integer> ]
|
|
11
11
|
def valid_response_codes
|
|
@@ -14,10 +14,13 @@ module WPScan
|
|
|
14
14
|
|
|
15
15
|
# @param [ Hash ] opts
|
|
16
16
|
# @option opts [ Range ] :range Mandatory
|
|
17
|
+
# @option opts [ Findings ] :found Shared findings collection; see
|
|
18
|
+
# {Plugins::KnownLocations#aggressive} for the streaming rationale.
|
|
17
19
|
#
|
|
18
20
|
# @return [ Array<User> ]
|
|
19
21
|
def aggressive(opts = {})
|
|
20
|
-
|
|
22
|
+
shared = opts[:found]
|
|
23
|
+
local = shared ? nil : []
|
|
21
24
|
found_by_msg = 'Author Id Brute Forcing - %s (Aggressive Detection)'
|
|
22
25
|
|
|
23
26
|
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, id|
|
|
@@ -25,15 +28,16 @@ module WPScan
|
|
|
25
28
|
|
|
26
29
|
next unless username
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
user = Model::User.new(
|
|
29
32
|
username,
|
|
30
33
|
id: id,
|
|
31
34
|
found_by: format(found_by_msg, found_by),
|
|
32
35
|
confidence: confidence
|
|
33
36
|
)
|
|
37
|
+
(shared || local) << user
|
|
34
38
|
end
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
local || []
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
# @param [ Hash ] opts
|
|
@@ -55,7 +59,7 @@ module WPScan
|
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
def full_request_params
|
|
58
|
-
{ followlocation: true }
|
|
62
|
+
{ followlocation: true, maxredirs: 10 }
|
|
59
63
|
end
|
|
60
64
|
|
|
61
65
|
# @param [ Typhoeus::Response ] res
|
|
@@ -68,7 +72,7 @@ module WPScan
|
|
|
68
72
|
|
|
69
73
|
username = display_name_from_body(res.body)
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
[username, 'Display Name', 50] if username
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
# @param [ String, Addressable::URI ] uri
|
|
@@ -5,7 +5,7 @@ module WPScan
|
|
|
5
5
|
module Users
|
|
6
6
|
# Since WP 5.5, /wp-sitemap-users-1.xml is generated and contains
|
|
7
7
|
# the usernames of accounts who made a post
|
|
8
|
-
class AuthorSitemap <
|
|
8
|
+
class AuthorSitemap < WPScan::Finders::Finder
|
|
9
9
|
# @param [ Hash ] opts
|
|
10
10
|
#
|
|
11
11
|
# @return [ Array<User> ]
|
|
@@ -10,7 +10,7 @@ module WPScan
|
|
|
10
10
|
# WP >= 3.1 - The password you entered for the username admin is incorrect.
|
|
11
11
|
# Non existent username: Invalid username.
|
|
12
12
|
#
|
|
13
|
-
class LoginErrorMessages <
|
|
13
|
+
class LoginErrorMessages < WPScan::Finders::Finder
|
|
14
14
|
# @param [ Hash ] opts
|
|
15
15
|
# @option opts [ String ] :list
|
|
16
16
|
#
|
|
@@ -5,7 +5,7 @@ module WPScan
|
|
|
5
5
|
module Users
|
|
6
6
|
# Since WP 4.4, the oembed API can disclose a user
|
|
7
7
|
# https://github.com/wpscanteam/wpscan/issues/1049
|
|
8
|
-
class OembedApi <
|
|
8
|
+
class OembedApi < WPScan::Finders::Finder
|
|
9
9
|
# @param [ Hash ] opts
|
|
10
10
|
#
|
|
11
11
|
# @return [ Array<User> ]
|
|
@@ -36,6 +36,8 @@ module WPScan
|
|
|
36
36
|
|
|
37
37
|
oembed_data = oembed_data.first if oembed_data.is_a?(Array)
|
|
38
38
|
|
|
39
|
+
oembed_data = {} unless oembed_data.is_a?(Hash)
|
|
40
|
+
|
|
39
41
|
if oembed_data['author_url'] =~ %r{/author/([^/]+)/?\z}
|
|
40
42
|
details = [Regexp.last_match[1], 'Author URL', 90]
|
|
41
43
|
elsif oembed_data['author_name'] && !oembed_data['author_name'].empty?
|
|
@@ -8,7 +8,7 @@ module WPScan
|
|
|
8
8
|
# Since 4.7 - Need more investigation as it seems WP 4.7.1 reduces the exposure, see https://github.com/wpscanteam/wpscan/issues/1038)
|
|
9
9
|
# For the pagination, see https://github.com/wpscanteam/wpscan/issues/1285
|
|
10
10
|
#
|
|
11
|
-
class WpJsonApi <
|
|
11
|
+
class WpJsonApi < WPScan::Finders::Finder
|
|
12
12
|
MAX_PER_PAGE = 100 # See https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
|
|
13
13
|
|
|
14
14
|
# @param [ Hash ] opts
|
|
@@ -42,12 +42,16 @@ module WPScan
|
|
|
42
42
|
def users_from_response(response)
|
|
43
43
|
found = []
|
|
44
44
|
|
|
45
|
-
JSON.parse(response.body)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
json = JSON.parse(response.body)
|
|
46
|
+
|
|
47
|
+
if json.is_a?(Enumerable)
|
|
48
|
+
json.each do |user|
|
|
49
|
+
found << Model::User.new(user['slug'],
|
|
50
|
+
id: user['id'],
|
|
51
|
+
found_by: found_by,
|
|
52
|
+
confidence: 100,
|
|
53
|
+
interesting_entries: [response.effective_url])
|
|
54
|
+
end
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
found
|
data/app/finders/users.rb
CHANGED
|
@@ -4,7 +4,7 @@ module WPScan
|
|
|
4
4
|
module Finders
|
|
5
5
|
module WpVersion
|
|
6
6
|
# Atom Generator Version Finder
|
|
7
|
-
class AtomGenerator <
|
|
7
|
+
class AtomGenerator < WPScan::Finders::Finder
|
|
8
8
|
include Finder::WpVersion::SmartURLChecker
|
|
9
9
|
|
|
10
10
|
def process_urls(urls, _opts = {})
|
|
@@ -4,7 +4,7 @@ module WPScan
|
|
|
4
4
|
module Finders
|
|
5
5
|
module WpVersion
|
|
6
6
|
# Readme Version Finder
|
|
7
|
-
class Readme <
|
|
7
|
+
class Readme < WPScan::Finders::Finder
|
|
8
8
|
# @return [ WpVersion ]
|
|
9
9
|
def aggressive(_opts = {})
|
|
10
10
|
readme_url = target.url('readme.html') # Maybe move this into the Target ?
|
|
@@ -4,8 +4,8 @@ module WPScan
|
|
|
4
4
|
module Finders
|
|
5
5
|
module WpVersion
|
|
6
6
|
# Unique Fingerprinting Version Finder
|
|
7
|
-
class UniqueFingerprinting <
|
|
8
|
-
include
|
|
7
|
+
class UniqueFingerprinting < WPScan::Finders::Finder
|
|
8
|
+
include WPScan::Finders::Finder::Fingerprinter
|
|
9
9
|
|
|
10
10
|
# @return [ WpVersion ]
|
|
11
11
|
def aggressive(opts = {})
|
data/app/finders/wp_version.rb
CHANGED
data/app/finders.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative 'finders/timthumb_version'
|
|
|
8
8
|
require_relative 'finders/timthumbs'
|
|
9
9
|
require_relative 'finders/config_backups'
|
|
10
10
|
require_relative 'finders/db_exports'
|
|
11
|
+
require_relative 'finders/backup_folders'
|
|
11
12
|
require_relative 'finders/medias'
|
|
12
13
|
require_relative 'finders/users'
|
|
13
14
|
require_relative 'finders/plugins'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
module Formatter
|
|
5
|
+
# CLI Formatter
|
|
6
|
+
class Cli < Base
|
|
7
|
+
def streams?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# ANSI sequence to clear the current line and return to column 0.
|
|
12
|
+
# Emitted at the start of each streamed enumeration finding so the
|
|
13
|
+
# ruby-progressbar line (which redraws via \r and leaves its previous
|
|
14
|
+
# render visible when interleaved with foreign output) gets wiped
|
|
15
|
+
# before the finding prints. No-op on non-TTY stdout so file/pipe
|
|
16
|
+
# output stays clean.
|
|
17
|
+
def bar_clear
|
|
18
|
+
$stdout.tty? ? "\e[2K\r" : ''
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [ String ]
|
|
22
|
+
def info_icon
|
|
23
|
+
green('[+]')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [ String ]
|
|
27
|
+
def notice_icon
|
|
28
|
+
blue('[i]')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [ String ]
|
|
32
|
+
def warning_icon
|
|
33
|
+
amber('[!]')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [ String ]
|
|
37
|
+
def critical_icon
|
|
38
|
+
red('[!]')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param [ String ] text
|
|
42
|
+
# @return [ String ]
|
|
43
|
+
def bold(text)
|
|
44
|
+
colorize(text, 1)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param [ String ] text
|
|
48
|
+
# @return [ String ]
|
|
49
|
+
def red(text)
|
|
50
|
+
colorize(text, 31)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param [ String ] text
|
|
54
|
+
# @return [ String ]
|
|
55
|
+
def green(text)
|
|
56
|
+
colorize(text, 32)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @param [ String ] text
|
|
60
|
+
# @return [ String ]
|
|
61
|
+
def amber(text)
|
|
62
|
+
colorize(text, 33)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @param [ String ] text
|
|
66
|
+
# @return [ String ]
|
|
67
|
+
def blue(text)
|
|
68
|
+
colorize(text, 34)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @param [ String ] text
|
|
72
|
+
# @param [ Integer ] color_code
|
|
73
|
+
# @return [ String ]
|
|
74
|
+
def colorize(text, color_code)
|
|
75
|
+
"\e[#{color_code}m#{text}\e[0m"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
module Formatter
|
|
5
|
+
# CLI No Colour Formatter
|
|
6
|
+
class CliNoColour < Cli
|
|
7
|
+
# Override to get the cli views
|
|
8
|
+
def format
|
|
9
|
+
'cli'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def colorize(text, _color_code)
|
|
13
|
+
text
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WPScan
|
|
4
|
+
module Formatter
|
|
5
|
+
# JSON Lines formatter — streams one self-contained JSON object per
|
|
6
|
+
# template render as the scan progresses. Reuses every app/views/json/*.erb
|
|
7
|
+
# template via base_format, so output content matches `-f json` but is
|
|
8
|
+
# emitted incrementally and can be piped directly into tools like `jq`.
|
|
9
|
+
class Jsonl < Base
|
|
10
|
+
def base_format
|
|
11
|
+
'json'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def streams?
|
|
15
|
+
true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def output(tpl, vars = {}, controller_name = nil)
|
|
19
|
+
rendered = render(tpl, vars, controller_name)
|
|
20
|
+
.encode('UTF-8', invalid: :replace, undef: :replace)
|
|
21
|
+
fragment = rendered.strip.chomp(',')
|
|
22
|
+
return if fragment.empty?
|
|
23
|
+
|
|
24
|
+
$stdout.puts JSON.generate(JSON.parse("{#{fragment}}"))
|
|
25
|
+
$stdout.flush
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|