wpscan 3.8.2 → 3.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/password_attack.rb +21 -21
- data/app/finders/interesting_findings/mu_plugins.rb +1 -1
- data/app/finders/interesting_findings/multisite.rb +2 -2
- data/app/finders/main_theme/css_style_in_homepage.rb +2 -2
- data/app/finders/passwords/wp_login.rb +1 -1
- data/app/finders/passwords/xml_rpc.rb +1 -1
- data/app/finders/passwords/xml_rpc_multicall.rb +35 -9
- data/app/finders/plugin_version/readme.rb +2 -2
- data/app/finders/theme_version/style.rb +1 -1
- data/app/finders/users.rb +1 -1
- data/app/finders/users/login_error_messages.rb +1 -1
- data/app/finders/users/rss_generator.rb +1 -1
- data/app/finders/users/yoast_seo_author_sitemap.rb +1 -1
- data/app/finders/wp_items/urls_in_page.rb +3 -3
- data/app/models/plugin.rb +1 -1
- data/app/models/theme.rb +2 -2
- data/app/models/wp_item.rb +1 -1
- data/app/models/wp_version.rb +1 -1
- data/lib/wpscan/errors/wordpress.rb +6 -0
- data/lib/wpscan/finders/dynamic_finder/version/config_parser.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/query_parameter.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/version/xpath.rb +1 -1
- data/lib/wpscan/finders/dynamic_finder/wp_version.rb +1 -1
- data/lib/wpscan/helper.rb +1 -1
- data/lib/wpscan/target.rb +4 -4
- data/lib/wpscan/target/platform/wordpress.rb +8 -7
- data/lib/wpscan/target/platform/wordpress/custom_directories.rb +3 -3
- data/lib/wpscan/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 658d14ca9901acb16c2da34ed977e5ad75296c4c8aba3e66ba897ffb5dd55f86
|
4
|
+
data.tar.gz: 788c5a27c5bf10dba5b35e977c3efdfafca97944485cf605f03e34faf65ae68b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be04200a71b1d710d47e0a8be2b914731803a48c94a8da7bfa1ad777de8f1e103fe9ac785589d70ca905c389c369bd4567118bae6d3585f88f7bf12c52c053af
|
7
|
+
data.tar.gz: 42f54d70d4aea2433c5b619abb5c516e731f7cfefafe9d4e0d8876956eb8470012e211b826c4790b1c7144ff091431906597b2e2a7c1cd63f25b07ab87013caf
|
@@ -23,27 +23,32 @@ module WPScan
|
|
23
23
|
]
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
if user_interaction?
|
30
|
-
output('@info',
|
31
|
-
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
32
|
-
end
|
33
|
-
|
34
|
-
attack_opts = {
|
26
|
+
def attack_opts
|
27
|
+
@attack_opts ||= {
|
35
28
|
show_progression: user_interaction?,
|
36
29
|
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
37
30
|
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
return unless ParsedCli.passwords
|
38
35
|
|
39
36
|
begin
|
40
37
|
found = []
|
41
38
|
|
42
|
-
|
39
|
+
if user_interaction?
|
40
|
+
output('@info',
|
41
|
+
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
42
|
+
end
|
43
|
+
|
44
|
+
attacker.attack(users, ParsedCli.passwords, attack_opts) do |user|
|
43
45
|
found << user
|
44
46
|
|
45
47
|
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
|
46
48
|
end
|
49
|
+
rescue Error::NoLoginInterfaceDetected => e
|
50
|
+
# TODO: Maybe output that in JSON as well.
|
51
|
+
output('@notice', msg: e.to_s) if user_interaction?
|
47
52
|
ensure
|
48
53
|
output('users', users: found)
|
49
54
|
end
|
@@ -65,6 +70,8 @@ module WPScan
|
|
65
70
|
|
66
71
|
case ParsedCli.password_attack
|
67
72
|
when :wp_login
|
73
|
+
raise Error::NoLoginInterfaceDetected unless target.login_url
|
74
|
+
|
68
75
|
Finders::Passwords::WpLogin.new(target)
|
69
76
|
when :xmlrpc
|
70
77
|
raise Error::XMLRPCNotDetected unless xmlrpc
|
@@ -82,7 +89,7 @@ module WPScan
|
|
82
89
|
if xmlrpc&.enabled? &&
|
83
90
|
xmlrpc.available_methods.include?('wp.getUsersBlogs') &&
|
84
91
|
xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
|
85
|
-
.run.body !~ /XML
|
92
|
+
.run.body !~ /XML-RPC services are disabled/
|
86
93
|
|
87
94
|
true
|
88
95
|
else
|
@@ -100,8 +107,10 @@ module WPScan
|
|
100
107
|
else
|
101
108
|
Finders::Passwords::XMLRPC.new(xmlrpc)
|
102
109
|
end
|
103
|
-
|
110
|
+
elsif target.login_url
|
104
111
|
Finders::Passwords::WpLogin.new(target)
|
112
|
+
else
|
113
|
+
raise Error::NoLoginInterfaceDetected
|
105
114
|
end
|
106
115
|
end
|
107
116
|
|
@@ -113,15 +122,6 @@ module WPScan
|
|
113
122
|
acc << Model::User.new(elem.chomp)
|
114
123
|
end
|
115
124
|
end
|
116
|
-
|
117
|
-
# @param [ String ] wordlist_path
|
118
|
-
#
|
119
|
-
# @return [ Array<String> ]
|
120
|
-
def passwords(wordlist_path)
|
121
|
-
@passwords ||= File.open(wordlist_path).reduce([]) do |acc, elem|
|
122
|
-
acc << elem.chomp
|
123
|
-
end
|
124
|
-
end
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
@@ -7,7 +7,7 @@ module WPScan
|
|
7
7
|
class MuPlugins < CMSScanner::Finders::Finder
|
8
8
|
# @return [ InterestingFinding ]
|
9
9
|
def passive(_opts = {})
|
10
|
-
pattern = %r{#{target.content_dir}/mu
|
10
|
+
pattern = %r{#{target.content_dir}/mu-plugins/}i
|
11
11
|
|
12
12
|
target.in_scope_uris(target.homepage_res, '(//@href|//@src)[contains(., "mu-plugins")]') do |uri|
|
13
13
|
next unless uri.path&.match?(pattern)
|
@@ -12,8 +12,8 @@ module WPScan
|
|
12
12
|
location = res.headers_hash['location']
|
13
13
|
|
14
14
|
return unless [200, 302].include?(res.code)
|
15
|
-
return if res.code == 302 && location
|
16
|
-
return unless res.code == 200 || res.code == 302 && location
|
15
|
+
return if res.code == 302 && location&.include?('wp-login.php?action=register')
|
16
|
+
return unless res.code == 200 || res.code == 302 && location&.include?('wp-signup.php')
|
17
17
|
|
18
18
|
target.multisite = true
|
19
19
|
|
@@ -21,7 +21,7 @@ module WPScan
|
|
21
21
|
|
22
22
|
def passive_from_css_href(res, opts)
|
23
23
|
target.in_scope_uris(res, '//link/@href[contains(., "style.css")]') do |uri|
|
24
|
-
next unless uri.path =~ %r{/themes/([
|
24
|
+
next unless uri.path =~ %r{/themes/([^/]+)/style.css\z}i
|
25
25
|
|
26
26
|
return create_theme(Regexp.last_match[1], uri.to_s, opts)
|
27
27
|
end
|
@@ -33,7 +33,7 @@ module WPScan
|
|
33
33
|
code = tag.text.to_s
|
34
34
|
next if code.empty?
|
35
35
|
|
36
|
-
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'
|
36
|
+
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'( ]*}i
|
37
37
|
|
38
38
|
return create_theme(Regexp.last_match[1], Regexp.last_match[0].strip, opts)
|
39
39
|
end
|
@@ -13,7 +13,7 @@ module WPScan
|
|
13
13
|
|
14
14
|
def valid_credentials?(response)
|
15
15
|
response.code == 302 &&
|
16
|
-
|
16
|
+
Array(response.headers['Set-Cookie'])&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
|
17
17
|
end
|
18
18
|
|
19
19
|
def errored_response?(response)
|
@@ -22,8 +22,30 @@ module WPScan
|
|
22
22
|
target.multi_call(methods, cache_ttl: 0).run
|
23
23
|
end
|
24
24
|
|
25
|
+
# @param [ IO ] file
|
26
|
+
# @param [ Integer ] passwords_size
|
27
|
+
# @return [ Array<String> ] The passwords from the last checked position in the file until there are
|
28
|
+
# passwords_size passwords retrieved
|
29
|
+
def passwords_from_wordlist(file, passwords_size)
|
30
|
+
pwds = []
|
31
|
+
added_pwds = 0
|
32
|
+
|
33
|
+
return pwds if passwords_size.zero?
|
34
|
+
|
35
|
+
# Make sure that the main code does not call #sysseek or #count etc
|
36
|
+
# otherwise the file descriptor will be set to somwehere else
|
37
|
+
file.each_line(chomp: true) do |line|
|
38
|
+
pwds << line
|
39
|
+
added_pwds += 1
|
40
|
+
|
41
|
+
break if added_pwds == passwords_size
|
42
|
+
end
|
43
|
+
|
44
|
+
pwds
|
45
|
+
end
|
46
|
+
|
25
47
|
# @param [ Array<Model::User> ] users
|
26
|
-
# @param [
|
48
|
+
# @param [ String ] wordlist_path
|
27
49
|
# @param [ Hash ] opts
|
28
50
|
# @option opts [ Boolean ] :show_progression
|
29
51
|
# @option opts [ Integer ] :multicall_max_passwords
|
@@ -33,18 +55,22 @@ module WPScan
|
|
33
55
|
# TODO: Make rubocop happy about metrics etc
|
34
56
|
#
|
35
57
|
# rubocop:disable all
|
36
|
-
def attack(users,
|
37
|
-
|
58
|
+
def attack(users, wordlist_path, opts = {})
|
59
|
+
checked_passwords = 0
|
60
|
+
wordlist = File.open(wordlist_path)
|
61
|
+
wordlist_size = wordlist.count
|
38
62
|
max_passwords = opts[:multicall_max_passwords]
|
39
63
|
current_passwords_size = passwords_size(max_passwords, users.size)
|
40
64
|
|
41
|
-
create_progress_bar(total: (
|
65
|
+
create_progress_bar(total: (wordlist_size / current_passwords_size.round(1)).ceil,
|
42
66
|
show_progression: opts[:show_progression])
|
43
67
|
|
68
|
+
wordlist.sysseek(0) # reset the descriptor to the beginning of the file as it changed with #count
|
69
|
+
|
44
70
|
loop do
|
45
|
-
current_users
|
46
|
-
current_passwords
|
47
|
-
|
71
|
+
current_users = users.select { |user| user.password.nil? }
|
72
|
+
current_passwords = passwords_from_wordlist(wordlist, current_passwords_size)
|
73
|
+
checked_passwords += current_passwords_size
|
48
74
|
|
49
75
|
break if current_users.empty? || current_passwords.nil? || current_passwords.empty?
|
50
76
|
|
@@ -75,9 +101,9 @@ module WPScan
|
|
75
101
|
progress_bar.stop
|
76
102
|
break
|
77
103
|
end
|
78
|
-
|
104
|
+
|
79
105
|
begin
|
80
|
-
progress_bar.total = progress_bar.progress + ((
|
106
|
+
progress_bar.total = progress_bar.progress + ((wordlist_size - checked_passwords) / current_passwords_size.round(1)).ceil
|
81
107
|
rescue ProgressBar::InvalidProgressError
|
82
108
|
end
|
83
109
|
end
|
@@ -48,7 +48,7 @@ module WPScan
|
|
48
48
|
#
|
49
49
|
# @return [ String, nil ] The version number detected from the stable tag
|
50
50
|
def from_stable_tag(body)
|
51
|
-
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z
|
51
|
+
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z.-]+)/i
|
52
52
|
|
53
53
|
number = Regexp.last_match[1]
|
54
54
|
|
@@ -59,7 +59,7 @@ module WPScan
|
|
59
59
|
#
|
60
60
|
# @return [ String, nil ] The best version number detected from the changelog section
|
61
61
|
def from_changelog_section(body)
|
62
|
-
extracted_versions = body.scan(%r{
|
62
|
+
extracted_versions = body.scan(%r{=+\s+(?:v(?:ersion)?\s*)?([0-9.-]+)[ \ta-z0-9().\-/]*=+}i)
|
63
63
|
|
64
64
|
return if extracted_versions.nil? || extracted_versions.empty?
|
65
65
|
|
@@ -30,7 +30,7 @@ module WPScan
|
|
30
30
|
|
31
31
|
# @return [ Version ]
|
32
32
|
def style_version
|
33
|
-
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z
|
33
|
+
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z.-]+)/i
|
34
34
|
|
35
35
|
Model::Version.new(
|
36
36
|
Regexp.last_match[1],
|
data/app/finders/users.rb
CHANGED
@@ -6,7 +6,7 @@ require_relative 'users/oembed_api'
|
|
6
6
|
require_relative 'users/rss_generator'
|
7
7
|
require_relative 'users/author_id_brute_forcing'
|
8
8
|
require_relative 'users/login_error_messages'
|
9
|
-
require_relative 'users/yoast_seo_author_sitemap
|
9
|
+
require_relative 'users/yoast_seo_author_sitemap'
|
10
10
|
|
11
11
|
module WPScan
|
12
12
|
module Finders
|
@@ -13,7 +13,7 @@ module WPScan
|
|
13
13
|
found = []
|
14
14
|
|
15
15
|
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
|
16
|
-
username = user_tag.text.to_s[%r{/author/([
|
16
|
+
username = user_tag.text.to_s[%r{/author/([^/]+)/}, 1]
|
17
17
|
|
18
18
|
next unless username && !username.strip.empty?
|
19
19
|
|
@@ -55,7 +55,7 @@ module WPScan
|
|
55
55
|
#
|
56
56
|
# @return [ Regexp ]
|
57
57
|
def item_code_pattern(type)
|
58
|
-
@item_code_pattern ||= %r{["'
|
58
|
+
@item_code_pattern ||= %r{["'( ]#{item_url_pattern(type)}([^\\/)"']+)}i
|
59
59
|
end
|
60
60
|
|
61
61
|
# @param [ String ] type
|
@@ -66,9 +66,9 @@ module WPScan
|
|
66
66
|
item_url = type == 'plugins' ? target.plugins_url : target.content_url
|
67
67
|
|
68
68
|
url = /#{item_url.gsub(/\A(?:https?)/i, 'https?').gsub('/', '\\\\\?\/')}/i
|
69
|
-
item_dir = %r{(?:#{url}
|
69
|
+
item_dir = %r{(?:#{url}|\\?/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
|
70
70
|
|
71
|
-
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}
|
71
|
+
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?/}i
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
data/app/models/plugin.rb
CHANGED
@@ -38,7 +38,7 @@ module WPScan
|
|
38
38
|
|
39
39
|
# @return [ Array<String> ]
|
40
40
|
def potential_readme_filenames
|
41
|
-
@potential_readme_filenames ||=
|
41
|
+
@potential_readme_filenames ||= Array((DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super))
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
data/app/models/theme.rb
CHANGED
@@ -45,7 +45,7 @@ module WPScan
|
|
45
45
|
# @return [ Theme ]
|
46
46
|
def parent_theme
|
47
47
|
return unless template
|
48
|
-
return unless style_body =~ /^@import\surl\(["']?([^"'
|
48
|
+
return unless style_body =~ /^@import\surl\(["']?([^"')]+)["']?\);\s*$/i
|
49
49
|
|
50
50
|
opts = detection_opts.merge(
|
51
51
|
style_url: url(Regexp.last_match[1]),
|
@@ -101,7 +101,7 @@ module WPScan
|
|
101
101
|
#
|
102
102
|
# @return [ String ]
|
103
103
|
def parse_style_tag(body, tag)
|
104
|
-
value = body[/#{Regexp.escape(tag)}:[\t ]*([^\r\n
|
104
|
+
value = body[/#{Regexp.escape(tag)}:[\t ]*([^\r\n*]+)/i, 1]
|
105
105
|
|
106
106
|
value && !value.strip.empty? ? value.strip : nil
|
107
107
|
end
|
data/app/models/wp_item.rb
CHANGED
@@ -39,7 +39,7 @@ module WPScan
|
|
39
39
|
|
40
40
|
@vulnerabilities = []
|
41
41
|
|
42
|
-
|
42
|
+
Array(db_data['vulnerabilities']).each do |json_vuln|
|
43
43
|
vulnerability = Vulnerability.load_from_json(json_vuln)
|
44
44
|
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
45
45
|
end
|
data/app/models/wp_version.rb
CHANGED
@@ -29,5 +29,11 @@ module WPScan
|
|
29
29
|
' use the --scope option or make sure the --url value given is the correct one'
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
class NoLoginInterfaceDetected < Standard
|
34
|
+
def to_s
|
35
|
+
'Could not find a login interface to perform the password attack against'
|
36
|
+
end
|
37
|
+
end
|
32
38
|
end
|
33
39
|
end
|
@@ -9,7 +9,7 @@ module WPScan
|
|
9
9
|
# @return [ Hash ]
|
10
10
|
def self.child_class_constants
|
11
11
|
@child_class_constants ||= super().merge(
|
12
|
-
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)
|
12
|
+
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)=(?<v>\d+\.[.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
data/lib/wpscan/helper.rb
CHANGED
@@ -13,7 +13,7 @@ end
|
|
13
13
|
#
|
14
14
|
# @return [ Symbol ]
|
15
15
|
def classify_slug(slug)
|
16
|
-
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(
|
16
|
+
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/-{1,}/, '_').camelize.to_s
|
17
17
|
classified = "D_#{classified}" if /\d/.match?(classified[0])
|
18
18
|
|
19
19
|
classified.to_sym
|
data/lib/wpscan/target.rb
CHANGED
@@ -19,13 +19,13 @@ module WPScan
|
|
19
19
|
# @return [ Boolean ]
|
20
20
|
def vulnerable?
|
21
21
|
[@wp_version, @main_theme, @plugins, @themes, @timthumbs].each do |e|
|
22
|
-
|
22
|
+
Array(e).each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
|
23
23
|
end
|
24
24
|
|
25
|
-
return true unless
|
26
|
-
return true unless
|
25
|
+
return true unless Array(@config_backups).empty?
|
26
|
+
return true unless Array(@db_exports).empty?
|
27
27
|
|
28
|
-
|
28
|
+
Array(@users).each { |u| return true if u.password }
|
29
29
|
|
30
30
|
false
|
31
31
|
end
|
@@ -11,9 +11,9 @@ module WPScan
|
|
11
11
|
module WordPress
|
12
12
|
include CMSScanner::Target::Platform::PHP
|
13
13
|
|
14
|
-
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu
|
15
|
-
WP_JSON_OEMBED_PATTERN = %r{/wp
|
16
|
-
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp
|
14
|
+
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu-)?plugins|uploads))|wp-includes)/}i.freeze
|
15
|
+
WP_JSON_OEMBED_PATTERN = %r{/wp-json/oembed/}i.freeze
|
16
|
+
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp-admin\\?/admin-ajax\.php}i.freeze
|
17
17
|
|
18
18
|
# These methods are used in the associated interesting_findings finders
|
19
19
|
# to keep the boolean state of the finding rather than re-check the whole thing again
|
@@ -139,15 +139,16 @@ module WPScan
|
|
139
139
|
# the first time the method is called, and the effective_url is then used
|
140
140
|
# if suitable, otherwise the default wp-login will be.
|
141
141
|
#
|
142
|
-
# @return [ String ] The URL to the login page
|
142
|
+
# @return [ String, false ] The URL to the login page or false if not detected
|
143
143
|
def login_url
|
144
|
-
return @login_url
|
144
|
+
return @login_url unless @login_url.nil?
|
145
145
|
|
146
|
-
@login_url = url('wp-login.php')
|
146
|
+
@login_url = url('wp-login.php') # TODO: url(ParsedCli.login_uri)
|
147
147
|
|
148
148
|
res = Browser.get_and_follow_location(@login_url)
|
149
149
|
|
150
|
-
@login_url = res.effective_url if res.effective_url =~ /wp
|
150
|
+
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
|
151
|
+
@login_url = false if res.code == 404
|
151
152
|
|
152
153
|
@login_url
|
153
154
|
end
|
@@ -104,7 +104,7 @@ module WPScan
|
|
104
104
|
return @sub_dir unless @sub_dir.nil?
|
105
105
|
|
106
106
|
# url_pattern is from CMSScanner::Target
|
107
|
-
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp
|
107
|
+
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp-includes/)}i
|
108
108
|
xpath = '(//@src|//@href|//@data-src)[contains(., "xmlrpc.php") or contains(., "wp-includes/")]'
|
109
109
|
|
110
110
|
[homepage_res, error_404_res].each do |page_res|
|
@@ -124,9 +124,9 @@ module WPScan
|
|
124
124
|
def url(path = nil)
|
125
125
|
return @uri.to_s unless path
|
126
126
|
|
127
|
-
if %r{wp
|
127
|
+
if %r{wp-content/plugins}i.match?(path)
|
128
128
|
path = +path.gsub('wp-content/plugins', plugins_dir)
|
129
|
-
elsif /wp
|
129
|
+
elsif /wp-content/i.match?(path)
|
130
130
|
path = +path.gsub('wp-content', content_dir)
|
131
131
|
elsif path[0] != '/' && sub_dir
|
132
132
|
path = "#{sub_dir}/#{path}"
|
data/lib/wpscan/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wpscan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.8.
|
4
|
+
version: 3.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WPScanTeam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cms_scanner
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.12.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.12.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,28 +100,28 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
103
|
+
version: 0.88.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
110
|
+
version: 0.88.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rubocop-performance
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.
|
117
|
+
version: 1.7.0
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.
|
124
|
+
version: 1.7.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: simplecov
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|