wpscan 3.7.2 → 3.7.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/custom_directories.rb +1 -1
- data/app/finders/users/author_id_brute_forcing.rb +4 -1
- data/app/finders/users/oembed_api.rb +2 -0
- data/app/finders/wp_items/urls_in_homepage.rb +6 -4
- data/app/models/theme.rb +1 -1
- data/lib/wpscan/target/platform/wordpress.rb +15 -7
- data/lib/wpscan/target/platform/wordpress/custom_directories.rb +3 -6
- data/lib/wpscan/version.rb +1 -1
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b09450bc3d471ad71176d276e4e1d5961ce9bc8b12511a133efe17145fe5206b
|
4
|
+
data.tar.gz: b31d1f2425c69100b985f660447e967d88a2706f737fe313d2517cc2ed3b726a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24e87decb82a9f01edcdf77b46fbecb7c816bef1b1ed2c7a3d839ef3f5975326920d1b3d6cd74684054744d56a8d0baa0f85f831c39477d9ac02355d9f47b8ca
|
7
|
+
data.tar.gz: 4c593ed0dd0bc0bb27bbf8b1827095d82ea601a25b17f673cc972c6e540ad21d13f61162d95c32b32fb06e7a93153264b432e5147caa779cfcbdba93d508b943
|
@@ -18,7 +18,7 @@ module WPScan
|
|
18
18
|
target.content_dir = ParsedCli.wp_content_dir if ParsedCli.wp_content_dir
|
19
19
|
target.plugins_dir = ParsedCli.wp_plugins_dir if ParsedCli.wp_plugins_dir
|
20
20
|
|
21
|
-
return if target.content_dir
|
21
|
+
return if target.content_dir
|
22
22
|
|
23
23
|
raise Error::WpContentDirNotDetected
|
24
24
|
end
|
@@ -97,9 +97,12 @@ module WPScan
|
|
97
97
|
# @return [ String, nil ]
|
98
98
|
def display_name_from_body(body)
|
99
99
|
page = Nokogiri::HTML.parse(body)
|
100
|
+
|
100
101
|
# WP >= 3.0
|
101
102
|
page.css('h1.page-title span').each do |node|
|
102
|
-
|
103
|
+
text = node.text.to_s.strip
|
104
|
+
|
105
|
+
return text unless text.empty?
|
103
106
|
end
|
104
107
|
|
105
108
|
# WP < 3.0
|
@@ -34,6 +34,8 @@ module WPScan
|
|
34
34
|
def user_details_from_oembed_data(oembed_data)
|
35
35
|
return unless oembed_data
|
36
36
|
|
37
|
+
oembed_data = oembed_data.first if oembed_data.is_a?(Array)
|
38
|
+
|
37
39
|
if oembed_data['author_url'] =~ %r{/author/([^/]+)/?\z}
|
38
40
|
details = [Regexp.last_match[1], 'Author URL', 90]
|
39
41
|
elsif oembed_data['author_name'] && !oembed_data['author_name'].empty?
|
@@ -15,7 +15,9 @@ module WPScan
|
|
15
15
|
target.in_scope_uris(target.homepage_res) do |uri|
|
16
16
|
next unless uri.to_s =~ item_attribute_pattern(type)
|
17
17
|
|
18
|
-
|
18
|
+
slug = Regexp.last_match[1]&.strip
|
19
|
+
|
20
|
+
found << slug unless slug&.empty?
|
19
21
|
end
|
20
22
|
|
21
23
|
uniq ? found.uniq.sort : found.sort
|
@@ -28,7 +30,7 @@ module WPScan
|
|
28
30
|
def items_from_codes(type, uniq = true)
|
29
31
|
found = []
|
30
32
|
|
31
|
-
target.homepage_res.html.
|
33
|
+
target.homepage_res.html.xpath('//script[not(@src)]|//style[not(@src)]').each do |tag|
|
32
34
|
code = tag.text.to_s
|
33
35
|
next if code.empty?
|
34
36
|
|
@@ -42,7 +44,7 @@ module WPScan
|
|
42
44
|
#
|
43
45
|
# @return [ Regexp ]
|
44
46
|
def item_attribute_pattern(type)
|
45
|
-
@item_attribute_pattern ||= %r{
|
47
|
+
@item_attribute_pattern ||= %r{#{item_url_pattern(type)}([^/]+)/}i
|
46
48
|
end
|
47
49
|
|
48
50
|
# @param [ String ] type
|
@@ -59,7 +61,7 @@ module WPScan
|
|
59
61
|
item_dir = type == 'plugins' ? target.plugins_dir : target.content_dir
|
60
62
|
item_url = type == 'plugins' ? target.plugins_url : target.content_url
|
61
63
|
|
62
|
-
url = /#{item_url.gsub(/\A(?:
|
64
|
+
url = /#{item_url.gsub(/\A(?:https?)/i, 'https?').gsub('/', '\\\\\?\/')}/i
|
63
65
|
item_dir = %r{(?:#{url}|\\?\/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
|
64
66
|
|
65
67
|
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?\/}i
|
data/app/models/theme.rb
CHANGED
@@ -101,7 +101,7 @@ module WPScan
|
|
101
101
|
#
|
102
102
|
# @return [ String ]
|
103
103
|
def parse_style_tag(body, tag)
|
104
|
-
value = body[
|
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
|
@@ -11,7 +11,9 @@ module WPScan
|
|
11
11
|
module WordPress
|
12
12
|
include CMSScanner::Target::Platform::PHP
|
13
13
|
|
14
|
-
WORDPRESS_PATTERN
|
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
|
15
17
|
|
16
18
|
# These methods are used in the associated interesting_findings finders
|
17
19
|
# to keep the boolean state of the finding rather than re-check the whole thing again
|
@@ -23,27 +25,33 @@ module WPScan
|
|
23
25
|
# @param [ Symbol ] detection_mode
|
24
26
|
#
|
25
27
|
# @return [ Boolean ]
|
28
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
26
29
|
def wordpress?(detection_mode)
|
27
30
|
in_scope_uris(homepage_res) do |uri|
|
28
|
-
return true if uri.path.match(
|
31
|
+
return true if WORDPRESS_PATTERN.match?(uri.path) || WP_JSON_OEMBED_PATTERN.match?(uri.path)
|
29
32
|
end
|
30
33
|
|
31
|
-
homepage_res.html.css('meta[name="generator"]').
|
32
|
-
|
34
|
+
return true if homepage_res.html.css('meta[name="generator"]').any? do |node|
|
35
|
+
/wordpress/i.match?(node['content'])
|
33
36
|
end
|
34
37
|
|
35
38
|
return true unless comments_from_page(/wordpress/i, homepage_res).empty?
|
36
39
|
|
40
|
+
return true if homepage_res.html.xpath('//script[not(@src)]').any? do |node|
|
41
|
+
WP_ADMIN_AJAX_PATTERN.match?(node.text)
|
42
|
+
end
|
43
|
+
|
37
44
|
if %i[mixed aggressive].include?(detection_mode)
|
38
45
|
%w[wp-admin/install.php wp-login.php].each do |path|
|
39
|
-
in_scope_uris(Browser.get_and_follow_location(url(path))).
|
40
|
-
|
46
|
+
return true if in_scope_uris(Browser.get_and_follow_location(url(path))).any? do |uri|
|
47
|
+
WORDPRESS_PATTERN.match?(uri.path)
|
41
48
|
end
|
42
49
|
end
|
43
50
|
end
|
44
51
|
|
45
52
|
false
|
46
53
|
end
|
54
|
+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
|
47
55
|
|
48
56
|
COOKIE_PATTERNS = {
|
49
57
|
'vjs' => /createCookie\('vjs','(?<c_value>\d+)',\d+\);/i
|
@@ -82,7 +90,7 @@ module WPScan
|
|
82
90
|
def wordpress_hosted?
|
83
91
|
return true if /\.wordpress\.com$/i.match?(uri.host)
|
84
92
|
|
85
|
-
unless content_dir
|
93
|
+
unless content_dir
|
86
94
|
pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
|
87
95
|
|
88
96
|
uris_from_page(homepage_res) do |uri|
|
@@ -13,12 +13,11 @@ module WPScan
|
|
13
13
|
@plugins_dir = dir.chomp('/')
|
14
14
|
end
|
15
15
|
|
16
|
-
# @param [ Symbol ] detection_mode
|
17
16
|
# @return [ String ] The wp-content directory
|
18
|
-
def content_dir
|
17
|
+
def content_dir
|
19
18
|
unless @content_dir
|
20
19
|
# scope_url_pattern is from CMSScanner::Target
|
21
|
-
pattern = %r{#{scope_url_pattern}([\w\s\-/]
|
20
|
+
pattern = %r{#{scope_url_pattern}([\w\s\-/]+?)\\?/(?:themes|plugins|uploads|cache)\\?/}i
|
22
21
|
|
23
22
|
in_scope_uris(homepage_res) do |uri|
|
24
23
|
return @content_dir = Regexp.last_match[1] if uri.to_s.match(pattern)
|
@@ -29,9 +28,7 @@ module WPScan
|
|
29
28
|
return @content_dir = match[1]
|
30
29
|
end
|
31
30
|
|
32
|
-
|
33
|
-
return @content_dir = 'wp-content' if default_content_dir_exists?
|
34
|
-
end
|
31
|
+
return @content_dir = 'wp-content' if default_content_dir_exists?
|
35
32
|
end
|
36
33
|
|
37
34
|
@content_dir
|
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.7.
|
4
|
+
version: 3.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WPScanTeam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cms_scanner
|
@@ -72,28 +72,28 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '13.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '13.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 3.
|
89
|
+
version: 3.9.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 3.
|
96
|
+
version: 3.9.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rspec-its
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,28 +114,28 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
117
|
+
version: 0.75.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: 0.
|
124
|
+
version: 0.75.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rubocop-performance
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 1.
|
131
|
+
version: 1.5.0
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 1.
|
138
|
+
version: 1.5.0
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: simplecov
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|