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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b58caaf90d2a23acc6f23bdb4506d09a34803a8af5b42409785cf33d4580d17
4
- data.tar.gz: 1001a14717f1051666ad6d923e977b901483c4e89dbc9c94d7a3bfffe4ea0ab5
3
+ metadata.gz: b09450bc3d471ad71176d276e4e1d5961ce9bc8b12511a133efe17145fe5206b
4
+ data.tar.gz: b31d1f2425c69100b985f660447e967d88a2706f737fe313d2517cc2ed3b726a
5
5
  SHA512:
6
- metadata.gz: daa9d7134979336b9c05eda0ec4cdd4cd269f11e9feebdda88c5c251aef8408670dae2ee433e578e210d0fa1cf710967ca13823a719904f548ab7ea04b9aabb9
7
- data.tar.gz: d7ca8fb4e69a5cfa592884c17d791ad155a334070e68b5d8becb222349d1e75397d617b2fa2b48be06d95c7910fafcb4884c7edc1cc78d3901b06eb6f80004e2
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(ParsedCli.detection_mode)
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
- return node.text.to_s
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
- found << Regexp.last_match[1]
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.css('script,style').each do |tag|
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{\A#{item_url_pattern(type)}([^/]+)/}i
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(?:http|https)/i, 'https?').gsub('/', '\\\\\?\/')}/i
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
@@ -101,7 +101,7 @@ module WPScan
101
101
  #
102
102
  # @return [ String ]
103
103
  def parse_style_tag(body, tag)
104
- value = body[/^\s*#{Regexp.escape(tag)}:[\t ]*([^\r\n]+)/i, 1]
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 = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i.freeze
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(WORDPRESS_PATTERN)
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"]').each do |node|
32
- return true if /wordpress/i.match?(node['content'])
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))).each do |uri|
40
- return true if uri.path.match(WORDPRESS_PATTERN)
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(:passive)
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(detection_mode = :mixed)
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\-/]+)\\?/(?:themes|plugins|uploads|cache)\\?/}i
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
- unless detection_mode == :passive
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Version
4
4
  module WPScan
5
- VERSION = '3.7.2'
5
+ VERSION = '3.7.3'
6
6
  end
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.2
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-09-25 00:00:00.000000000 Z
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: '12.3'
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: '12.3'
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.8.0
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.8.0
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.74.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.74.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.4.0
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.4.0
138
+ version: 1.5.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: simplecov
141
141
  requirement: !ruby/object:Gem::Requirement