cms_scanner 0.10.1 → 0.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee896090149c3f5dfd501ce88374b708aba187927bdac0c593f3608c801ae0d3
4
- data.tar.gz: 24f17ebbab747417216229057783c1545ebbe06ff0a4c6c6e2ff969b67e69f97
3
+ metadata.gz: 72a6af6653b611521780cf690b877be65574e84416fd85f6cb510252cbd63f37
4
+ data.tar.gz: 9004450478afb99a08c26e7fc6256d4c614491fe2fae1c91cf23a95d36583f5c
5
5
  SHA512:
6
- metadata.gz: 13f907d61456e051f39e2988755537e6ad6ccdbd473144a5e756cb9f91a680d23539ced35cb9e29925f7d1832e4d84fe8d975871c4d5aa230c863a9e92133eee
7
- data.tar.gz: 6fd87965f3fe9385583f5c8fef0ea82fc7b03c8a803e68b6117f410dac554b0c8677d975b894b37cf4c29e87260cb06f2d2e17c75c8e873912060d5707fe2804
6
+ metadata.gz: 23a881867063dc089d9bc6efe581c3e631ead8ac0b09408d085ba54d4cd5584da9988cc947dbdeb707a2b74bfde3408c1e48f025942d782e7427ccf64d6380f1
7
+ data.tar.gz: 0cda187347c63f48bb1bea47f2cb2d45089aa38aaa885e6c5a4e32384409e7b2eb71453187b7db0531e1850ed3d48390cd823b53fcd93e7f531ee386cd9646a1
@@ -43,7 +43,7 @@ module CMSScanner
43
43
  when 401
44
44
  raise Error::HTTPAuthRequired
45
45
  when 403
46
- raise Error::AccessForbidden, NS::ParsedCli.random_user_agent
46
+ raise Error::AccessForbidden, NS::ParsedCli.random_user_agent unless NS::ParsedCli.force
47
47
  when 407
48
48
  raise Error::ProxyAuthRequired
49
49
  end
@@ -10,7 +10,8 @@ module CMSScanner
10
10
  [
11
11
  OptURL.new(['-u', '--url URL', 'The URL to scan'],
12
12
  required_unless: %i[help hh version],
13
- default_protocol: 'http')
13
+ default_protocol: 'http'),
14
+ OptBoolean.new(['--force', 'Do not check if target returns a 403'])
14
15
  ] + mixed_cli_options + [
15
16
  OptFilePath.new(['-o', '--output FILE', 'Output to FILE'], writable: true, exists: false),
16
17
  OptChoice.new(['-f', '--format FORMAT',
@@ -2,8 +2,16 @@
2
2
 
3
3
  module CMSScanner
4
4
  module Model
5
- # FantasticoFileslist
5
+ # Fantastico is a commercial script library that automates the installation of web applications to a website.
6
+ # Fantastico scripts are executed from the administration area of a website control panel such as cPanel.
7
+ # It creates a file named fantastico_fileslist.txt that is publicly available and contains a list of all the
8
+ # files from the current directory. The contents of this file may expose sensitive information to an attacker.
6
9
  class FantasticoFileslist < InterestingFinding
10
+ # @return [ String ]
11
+ def to_s
12
+ @to_s ||= "Fantastico list found: #{url}"
13
+ end
14
+
7
15
  # @return [ Array<String> ] The interesting files/dirs detected
8
16
  def interesting_entries
9
17
  results = []
@@ -17,7 +25,9 @@ module CMSScanner
17
25
  end
18
26
 
19
27
  def references
20
- @references ||= { url: ['http://www.acunetix.com/vulnerabilities/fantastico-fileslist/'] }
28
+ @references ||= {
29
+ url: ['https://web.archive.org/web/20140518040021/http://www.acunetix.com/vulnerabilities/fantastico-fileslist/']
30
+ }
21
31
  end
22
32
  end
23
33
  end
@@ -19,7 +19,7 @@ module CMSScanner
19
19
  entries.each do |header, value|
20
20
  next if known_headers.include?(header.downcase)
21
21
 
22
- results << "#{header}: #{[*value].join(', ')}"
22
+ results << "#{header}: #{Array(value).join(', ')}"
23
23
  end
24
24
  results
25
25
  end
@@ -4,6 +4,11 @@ module CMSScanner
4
4
  module Model
5
5
  # Robots.txt
6
6
  class RobotsTxt < InterestingFinding
7
+ # @return [ String ]
8
+ def to_s
9
+ @to_s ||= "robots.txt found: #{url}"
10
+ end
11
+
7
12
  # @todo Better detection, currently everything not empty or / is returned
8
13
  #
9
14
  # @return [ Array<String> ] The interesting Allow/Disallow rules detected
@@ -4,6 +4,11 @@ module CMSScanner
4
4
  module Model
5
5
  # SearchReplaceDB2
6
6
  class SearchReplaceDB2 < InterestingFinding
7
+ # @return [ String ]
8
+ def to_s
9
+ @to_s ||= "Search Replace DB script found: #{url}"
10
+ end
11
+
7
12
  def references
8
13
  @references ||= { url: ['https://interconnectit.com/products/search-and-replace-for-wordpress-databases/'] }
9
14
  end
@@ -1,4 +1,5 @@
1
1
  "scan_aborted": <%= @reason.to_json %>,
2
+ "target_url": <%= @url.to_json %>,
2
3
  <% if @verbose -%>
3
4
  "trace": <%= @trace.to_json %>,
4
5
  <% end %>
@@ -59,7 +59,7 @@ module CMSScanner
59
59
  #
60
60
  # @return [ String ] The expiration file path associated to the key
61
61
  def entry_expiration_path(key)
62
- entry_path(key) + '.expiration'
62
+ "#{entry_path(key)}.expiration"
63
63
  end
64
64
 
65
65
  private
@@ -44,7 +44,7 @@ module CMSScanner
44
44
 
45
45
  def to_s
46
46
  msg = if random_user_agent_used
47
- 'Well... --random-user-agent didn\'t work, you\'re on your own now!'
47
+ 'Well... --random-user-agent didn\'t work, use --force to skip this check if needed.'
48
48
  else
49
49
  'Please re-try with --random-user-agent'
50
50
  end
@@ -21,14 +21,14 @@ module CMSScanner
21
21
 
22
22
  return symbols if mode.nil? || mode == :mixed
23
23
 
24
- symbols.include?(mode) ? [*mode] : []
24
+ symbols.include?(mode) ? Array(mode) : []
25
25
  end
26
26
 
27
27
  # @param [ CMSScanner::Finders::Finder ] finder
28
28
  # @param [ Symbol ] symbol See return values of #symbols_from_mode
29
29
  # @param [ Hash ] opts
30
30
  def run_finder(finder, symbol, opts)
31
- [*finder.send(symbol, opts.merge(found: findings))].compact.each do |found|
31
+ Array(finder.send(symbol, opts.merge(found: findings))).compact.each do |found|
32
32
  findings << found
33
33
  end
34
34
  end
@@ -57,10 +57,12 @@ module CMSScanner
57
57
  # @param [String, Class ] klass
58
58
  # @return [ String ]
59
59
  def found_by(klass = self.class)
60
+ labels = %w[aggressive passive]
61
+
60
62
  caller_locations.each do |call|
61
63
  label = call.label
62
64
 
63
- next unless %w[aggressive passive].include? label
65
+ next unless labels.include? label
64
66
 
65
67
  title = klass.to_s.demodulize.gsub(/(\d+)[a-z]+/i, '_\0').titleize(keep_id_suffix: true)
66
68
 
@@ -6,20 +6,22 @@ module CMSScanner
6
6
  # Module to provide an easy way to perform password attacks
7
7
  module BreadthFirstDictionaryAttack
8
8
  # @param [ Array<CMSScanner::Model::User> ] users
9
- # @param [ Array<String> ] passwords
9
+ # @param [ String ] wordlist_path
10
10
  # @param [ Hash ] opts
11
11
  # @option opts [ Boolean ] :show_progression
12
12
  #
13
13
  # @yield [ CMSScanner::User ] When a valid combination is found
14
14
  #
15
15
  # Due to Typhoeus threads shenanigans, in rare cases the progress-bar might
16
- # be incorrect updated, hence the 'rescue ProgressBar::InvalidProgressError'
16
+ # be incorrectly updated, hence the 'rescue ProgressBar::InvalidProgressError'
17
17
  #
18
18
  # TODO: Make rubocop happy about metrics etc
19
19
  #
20
20
  # rubocop:disable all
21
- def attack(users, passwords, opts = {})
22
- create_progress_bar(total: users.size * passwords.size, show_progression: opts[:show_progression])
21
+ def attack(users, wordlist_path, opts = {})
22
+ wordlist = File.open(wordlist_path)
23
+
24
+ create_progress_bar(total: users.size * wordlist.count, show_progression: opts[:show_progression])
23
25
 
24
26
  queue_count = 0
25
27
  # Keep the number of requests sent for each users
@@ -28,7 +30,7 @@ module CMSScanner
28
30
 
29
31
  users.each { |u| user_requests_count[u.username] = 0 }
30
32
 
31
- passwords.each do |password|
33
+ File.foreach(wordlist, chomp: true) do |password|
32
34
  remaining_users = users.select { |u| u.password.nil? }
33
35
 
34
36
  break if remaining_users.empty?
@@ -47,7 +49,7 @@ module CMSScanner
47
49
  user.password = password
48
50
 
49
51
  begin
50
- progress_bar.total -= passwords.size - user_requests_count[user.username]
52
+ progress_bar.total -= wordlist.count - user_requests_count[user.username]
51
53
  rescue ProgressBar::InvalidProgressError
52
54
  end
53
55
 
@@ -105,8 +107,10 @@ module CMSScanner
105
107
  "No response from remote server. WAF/IPS? (#{response.return_message})"
106
108
  elsif response.code.to_s.start_with?('50')
107
109
  'Server error, try reducing the number of threads.'
108
- else
110
+ elsif NS::ParsedCli.verbose?
109
111
  "Unknown response received Code: #{response.code}\nBody: #{response.body}"
112
+ else
113
+ "Unknown response received Code: #{response.code}"
110
114
  end
111
115
 
112
116
  progress_bar.log("Error: #{error}")
@@ -55,7 +55,7 @@ module CMSScanner
55
55
  # @return [ Typhoeus::Response, nil ]
56
56
  def maybe_get_full_response(head_res, opts)
57
57
  return head_res unless opts[:check_full_response] == true ||
58
- [*opts[:check_full_response]].include?(head_res.code)
58
+ Array(opts[:check_full_response]).include?(head_res.code)
59
59
 
60
60
  full_res = NS::Browser.get(head_res.effective_url, full_request_params)
61
61
 
@@ -8,6 +8,6 @@ class Numeric
8
8
  e = abs.zero? ? abs : (Math.log(abs) / Math.log(1024)).floor
9
9
  s = format('%<s>.3f', s: (abs.to_f / 1024**e))
10
10
 
11
- s.sub(/\.?0*$/, ' ' + units[e])
11
+ s.sub(/\.?0*$/, " #{units[e]}")
12
12
  end
13
13
  end
@@ -17,7 +17,7 @@ module CMSScanner
17
17
  def log(string = nil)
18
18
  return logs if string.nil?
19
19
 
20
- logs << string
20
+ logs << string unless logs.include?(string)
21
21
  end
22
22
  end
23
23
  end
@@ -21,9 +21,9 @@ module CMSScanner
21
21
  next unless refs.key?(key)
22
22
 
23
23
  @references[key] = if key == :youtube
24
- [*refs[:youtube]].map { |id| youtube_url(id) }
24
+ Array(refs[:youtube]).map { |id| youtube_url(id) }
25
25
  else
26
- [*refs[key]].map(&:to_s)
26
+ Array(refs[key]).map(&:to_s)
27
27
  end
28
28
  end
29
29
  end
@@ -30,6 +30,7 @@ module CMSScanner
30
30
  @run_error = e
31
31
 
32
32
  formatter.output('@scan_aborted',
33
+ url: controllers.first.target.url,
33
34
  reason: e.is_a?(Interrupt) ? 'Canceled by User' : e.message,
34
35
  trace: e.backtrace,
35
36
  verbose: NS::ParsedCli.verbose ||
@@ -18,7 +18,7 @@ module CMSScanner
18
18
  super(url, opts)
19
19
 
20
20
  scope << uri.host
21
- [*opts[:scope]].each { |s| scope << s }
21
+ Array(opts[:scope]).each { |s| scope << s }
22
22
  end
23
23
 
24
24
  # @param [ Hash ] opts
@@ -105,11 +105,11 @@ module CMSScanner
105
105
  next unless attr_value && !attr_value.empty?
106
106
 
107
107
  node_uri = begin
108
- uri.join(attr_value.strip)
109
- rescue StandardError
110
- # Skip potential malformed URLs etc.
111
- next
112
- end
108
+ uri.join(attr_value.strip)
109
+ rescue StandardError
110
+ # Skip potential malformed URLs etc.
111
+ next
112
+ end
113
113
 
114
114
  next unless node_uri.host
115
115
 
@@ -5,7 +5,7 @@ module CMSScanner
5
5
  module Platform
6
6
  # Some PHP specific implementation
7
7
  module PHP
8
- DEBUG_LOG_PATTERN = /(?:\[\d{2}\-[a-zA-Z]{3}\-\d{4}\s\d{2}\:\d{2}:\d{2}\s[A-Z]{3}\]|
8
+ DEBUG_LOG_PATTERN = /(?:\[\d{2}-[a-zA-Z]{3}-\d{4}\s\d{2}:\d{2}:\d{2}\s[A-Z]{3}\]|
9
9
  PHP\s(?:Fatal|Warning|Strict|Error|Notice):)/x.freeze
10
10
  FPD_PATTERN = /Fatal error:.+? in (.+?) on/.freeze
11
11
  ERROR_LOG_PATTERN = /PHP Fatal error/i.freeze
@@ -53,14 +53,14 @@ module CMSScanner
53
53
  domains = [uri.host + uri.path]
54
54
 
55
55
  domains += if scope.domains.empty?
56
- [*scope.invalid_domains[1..-1]]
56
+ Array(scope.invalid_domains[1..-1])
57
57
  else
58
- [*scope.domains[1..-1]].map(&:to_s) + scope.invalid_domains
58
+ Array(scope.domains[1..-1]).map(&:to_s) + scope.invalid_domains
59
59
  end
60
60
 
61
61
  domains.map! { |d| Regexp.escape(d.delete_suffix('/')).gsub('\*', '.*').gsub('/', '\\\\\?/') }
62
62
 
63
- domains[0].gsub!(Regexp.escape(uri.host), Regexp.escape(uri.host) + '(?::\\d+)?') if uri.port
63
+ domains[0].gsub!(Regexp.escape(uri.host), "#{Regexp.escape(uri.host)}(?::\\d+)?") if uri.port
64
64
 
65
65
  @scope_url_pattern = %r{https?:\\?/\\?/(?:#{domains.join('|')})\\?/?}i
66
66
  end
@@ -41,7 +41,7 @@ module CMSScanner
41
41
  def directory_listing?(path = nil, params = {})
42
42
  res = NS::Browser.get(url(path), params)
43
43
 
44
- res.code == 200 && res.body =~ /<h1>Index of/ ? true : false
44
+ res.code == 200 && res.body.include?('<h1>Index of') ? true : false
45
45
  end
46
46
 
47
47
  # @param [ String ] path
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Version
4
4
  module CMSScanner
5
- VERSION = '0.10.1'
5
+ VERSION = '0.13.0'
6
6
  end
@@ -62,7 +62,7 @@ module CMSScanner
62
62
 
63
63
  # @return [ String ] The URL of an unlikely existant page
64
64
  def error_404_url
65
- @error_404_url ||= uri.join(Digest::MD5.hexdigest(rand(999_999).to_s)[0..6] + '.html').to_s
65
+ @error_404_url ||= uri.join("#{Digest::MD5.hexdigest(rand(999_999).to_s)[0..6]}.html").to_s
66
66
  end
67
67
 
68
68
  # Checks if the remote website is up.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cms_scanner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - WPScanTeam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-08 00:00:00.000000000 Z
11
+ date: 2021-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: get_process_mem
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.10.8
33
+ version: 1.11.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.10.8
40
+ version: 1.11.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: opt_parse_validator
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.9.1
47
+ version: 1.9.3
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.9.1
54
+ version: 1.9.3
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: public_suffix
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +70,22 @@ dependencies:
70
70
  name: ruby-progressbar
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1.10'
76
+ - - "<"
74
77
  - !ruby/object:Gem::Version
75
- version: 1.10.0
78
+ version: '1.12'
76
79
  type: :runtime
77
80
  prerelease: false
78
81
  version_requirements: !ruby/object:Gem::Requirement
79
82
  requirements:
80
- - - "~>"
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '1.10'
86
+ - - "<"
81
87
  - !ruby/object:Gem::Version
82
- version: 1.10.0
88
+ version: '1.12'
83
89
  - !ruby/object:Gem::Dependency
84
90
  name: typhoeus
85
91
  requirement: !ruby/object:Gem::Requirement
@@ -176,14 +182,14 @@ dependencies:
176
182
  requirements:
177
183
  - - "~>"
178
184
  - !ruby/object:Gem::Version
179
- version: 3.9.0
185
+ version: 3.10.0
180
186
  type: :development
181
187
  prerelease: false
182
188
  version_requirements: !ruby/object:Gem::Requirement
183
189
  requirements:
184
190
  - - "~>"
185
191
  - !ruby/object:Gem::Version
186
- version: 3.9.0
192
+ version: 3.10.0
187
193
  - !ruby/object:Gem::Dependency
188
194
  name: rspec-its
189
195
  requirement: !ruby/object:Gem::Requirement
@@ -204,42 +210,42 @@ dependencies:
204
210
  requirements:
205
211
  - - "~>"
206
212
  - !ruby/object:Gem::Version
207
- version: 0.85.0
213
+ version: 1.8.0
208
214
  type: :development
209
215
  prerelease: false
210
216
  version_requirements: !ruby/object:Gem::Requirement
211
217
  requirements:
212
218
  - - "~>"
213
219
  - !ruby/object:Gem::Version
214
- version: 0.85.0
220
+ version: 1.8.0
215
221
  - !ruby/object:Gem::Dependency
216
222
  name: rubocop-performance
217
223
  requirement: !ruby/object:Gem::Requirement
218
224
  requirements:
219
225
  - - "~>"
220
226
  - !ruby/object:Gem::Version
221
- version: 1.6.0
227
+ version: 1.9.0
222
228
  type: :development
223
229
  prerelease: false
224
230
  version_requirements: !ruby/object:Gem::Requirement
225
231
  requirements:
226
232
  - - "~>"
227
233
  - !ruby/object:Gem::Version
228
- version: 1.6.0
234
+ version: 1.9.0
229
235
  - !ruby/object:Gem::Dependency
230
236
  name: simplecov
231
237
  requirement: !ruby/object:Gem::Requirement
232
238
  requirements:
233
239
  - - "~>"
234
240
  - !ruby/object:Gem::Version
235
- version: 0.18.2
241
+ version: 0.21.0
236
242
  type: :development
237
243
  prerelease: false
238
244
  version_requirements: !ruby/object:Gem::Requirement
239
245
  requirements:
240
246
  - - "~>"
241
247
  - !ruby/object:Gem::Version
242
- version: 0.18.2
248
+ version: 0.21.0
243
249
  - !ruby/object:Gem::Dependency
244
250
  name: simplecov-lcov
245
251
  requirement: !ruby/object:Gem::Requirement
@@ -260,14 +266,14 @@ dependencies:
260
266
  requirements:
261
267
  - - "~>"
262
268
  - !ruby/object:Gem::Version
263
- version: 3.8.0
269
+ version: 3.11.0
264
270
  type: :development
265
271
  prerelease: false
266
272
  version_requirements: !ruby/object:Gem::Requirement
267
273
  requirements:
268
274
  - - "~>"
269
275
  - !ruby/object:Gem::Version
270
- version: 3.8.0
276
+ version: 3.11.0
271
277
  description: Framework to provide an easy way to implement CMS Scanners
272
278
  email:
273
279
  - team@wpscan.org