cms_scanner 0.0.41.10 → 0.0.42.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/app/app.rb +23 -4
  3. data/app/controllers/core.rb +8 -6
  4. data/app/controllers/core/cli_options.rb +2 -0
  5. data/app/controllers/interesting_findings.rb +2 -0
  6. data/app/finders/interesting_findings.rb +2 -0
  7. data/app/finders/interesting_findings/fantastico_fileslist.rb +6 -8
  8. data/app/finders/interesting_findings/headers.rb +3 -1
  9. data/app/finders/interesting_findings/robots_txt.rb +5 -7
  10. data/app/finders/interesting_findings/search_replace_db_2.rb +8 -10
  11. data/app/finders/interesting_findings/xml_rpc.rb +8 -6
  12. data/app/formatters/cli.rb +2 -0
  13. data/app/formatters/cli_no_color.rb +2 -0
  14. data/app/formatters/cli_no_colour.rb +2 -0
  15. data/app/formatters/json.rb +2 -0
  16. data/app/models/fantastico_fileslist.rb +16 -12
  17. data/app/models/headers.rb +29 -25
  18. data/app/models/interesting_finding.rb +44 -40
  19. data/app/models/robots_txt.rb +18 -14
  20. data/app/models/user.rb +25 -21
  21. data/app/models/version.rb +45 -41
  22. data/app/models/xml_rpc.rb +58 -54
  23. data/lib/cms_scanner.rb +5 -85
  24. data/lib/cms_scanner/browser.rb +2 -0
  25. data/lib/cms_scanner/browser/actions.rb +13 -13
  26. data/lib/cms_scanner/browser/options.rb +2 -0
  27. data/lib/cms_scanner/cache/file_store.rb +2 -0
  28. data/lib/cms_scanner/cache/typhoeus.rb +2 -0
  29. data/lib/cms_scanner/controller.rb +2 -0
  30. data/lib/cms_scanner/controllers.rb +3 -1
  31. data/lib/cms_scanner/errors.rb +11 -0
  32. data/lib/cms_scanner/errors/http.rb +52 -51
  33. data/lib/cms_scanner/errors/scan.rb +10 -6
  34. data/lib/cms_scanner/exit_code.rb +2 -0
  35. data/lib/cms_scanner/finders.rb +2 -0
  36. data/lib/cms_scanner/finders/base_finders.rb +2 -0
  37. data/lib/cms_scanner/finders/finder.rb +3 -1
  38. data/lib/cms_scanner/finders/finder/breadth_first_dictionary_attack.rb +3 -1
  39. data/lib/cms_scanner/finders/finder/enumerator.rb +44 -15
  40. data/lib/cms_scanner/finders/finder/fingerprinter.rb +9 -21
  41. data/lib/cms_scanner/finders/finder/smart_url_checker.rb +2 -0
  42. data/lib/cms_scanner/finders/finder/smart_url_checker/findings.rb +2 -0
  43. data/lib/cms_scanner/finders/finding.rb +2 -0
  44. data/lib/cms_scanner/finders/findings.rb +2 -0
  45. data/lib/cms_scanner/finders/independent_finder.rb +2 -0
  46. data/lib/cms_scanner/finders/independent_finders.rb +2 -0
  47. data/lib/cms_scanner/finders/same_type_finder.rb +2 -0
  48. data/lib/cms_scanner/finders/same_type_finders.rb +2 -0
  49. data/lib/cms_scanner/finders/unique_finder.rb +2 -0
  50. data/lib/cms_scanner/finders/unique_finders.rb +2 -0
  51. data/lib/cms_scanner/formatter.rb +2 -0
  52. data/lib/cms_scanner/formatter/buffer.rb +3 -1
  53. data/lib/cms_scanner/helper.rb +2 -0
  54. data/lib/cms_scanner/numeric.rb +2 -0
  55. data/lib/cms_scanner/progressbar_null_output.rb +2 -0
  56. data/lib/cms_scanner/public_suffix/domain.rb +2 -0
  57. data/lib/cms_scanner/references.rb +2 -0
  58. data/lib/cms_scanner/scan.rb +86 -0
  59. data/lib/cms_scanner/target.rb +2 -0
  60. data/lib/cms_scanner/target/hashes.rb +2 -0
  61. data/lib/cms_scanner/target/platform.rb +2 -0
  62. data/lib/cms_scanner/target/platform/php.rb +4 -2
  63. data/lib/cms_scanner/target/scope.rb +2 -0
  64. data/lib/cms_scanner/target/server.rb +2 -0
  65. data/lib/cms_scanner/target/server/apache.rb +2 -0
  66. data/lib/cms_scanner/target/server/generic.rb +2 -0
  67. data/lib/cms_scanner/target/server/iis.rb +2 -0
  68. data/lib/cms_scanner/target/server/nginx.rb +2 -0
  69. data/lib/cms_scanner/typhoeus/hydra.rb +2 -0
  70. data/lib/cms_scanner/typhoeus/response.rb +2 -0
  71. data/lib/cms_scanner/version.rb +3 -1
  72. data/lib/cms_scanner/vulnerability.rb +2 -0
  73. data/lib/cms_scanner/web_site.rb +34 -2
  74. metadata +4 -6
  75. data/app/controllers.rb +0 -2
  76. data/app/finders.rb +0 -1
  77. data/app/formatters.rb +0 -4
  78. data/app/models.rb +0 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  # Options available in the Browser
3
5
  class Browser
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Cache
3
5
  # Cache Implementation using files
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cms_scanner/cache/file_store'
2
4
 
3
5
  module CMSScanner
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Controller
3
5
  # Base Controller
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  # Controllers Container
3
5
  class Controllers < Array
@@ -39,7 +41,7 @@ module CMSScanner
39
41
 
40
42
  redirect_output_to_file(parsed_options[:output]) if parsed_options[:output]
41
43
 
42
- Timeout.timeout(parsed_options[:max_scan_duration], NS::MaxScanDurationReachedError) do
44
+ Timeout.timeout(parsed_options[:max_scan_duration], NS::Error::MaxScanDurationReached) do
43
45
  each(&:before_scan)
44
46
  each(&:run)
45
47
  # Reverse is used here as the app/controllers/core#after_scan finishes the output
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Error
5
+ class Standard < StandardError
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'errors/http'
11
+ require_relative 'errors/scan'
@@ -1,70 +1,71 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
- class Error < StandardError
3
- end
4
+ module Error
5
+ # Target Down Error
6
+ class TargetDown < Standard
7
+ attr_reader :response
4
8
 
5
- # Target Down Error
6
- class TargetDownError < Error
7
- attr_reader :response
9
+ def initialize(response)
10
+ @response = response
11
+ end
8
12
 
9
- def initialize(response)
10
- @response = response
13
+ def to_s
14
+ "The url supplied '#{response.request.url}' seems to be down (#{response.return_message})"
15
+ end
11
16
  end
12
17
 
13
- def to_s
14
- "The url supplied '#{response.request.url}' seems to be down (#{response.return_message})"
15
- end
16
- end
17
-
18
- # HTTP Authentication Required Error
19
- class HTTPAuthRequiredError < Error
20
- # :nocov:
21
- def to_s
22
- 'HTTP authentication required (or was invalid), please provide it with --http-auth'
18
+ # HTTP Authentication Required Error
19
+ class HTTPAuthRequired < Standard
20
+ # :nocov:
21
+ def to_s
22
+ 'HTTP authentication required (or was invalid), please provide it with --http-auth'
23
+ end
24
+ # :nocov:
23
25
  end
24
- # :nocov:
25
- end
26
26
 
27
- # Proxy Authentication Required Error
28
- class ProxyAuthRequiredError < Error
29
- # :nocov:
30
- def to_s
31
- 'Proxy authentication required (or was invalid), please provide it with --proxy-auth'
27
+ # Proxy Authentication Required Error
28
+ class ProxyAuthRequired < Standard
29
+ # :nocov:
30
+ def to_s
31
+ 'Proxy authentication required (or was invalid), please provide it with --proxy-auth'
32
+ end
33
+ # :nocov:
32
34
  end
33
- # :nocov:
34
- end
35
35
 
36
- # Access Forbidden Error
37
- class AccessForbiddenError < Error
38
- attr_reader :random_user_agent_used
36
+ # Access Forbidden Error
37
+ class AccessForbidden < Standard
38
+ attr_reader :random_user_agent_used
39
39
 
40
- # @param [ Boolean ] random_user_agent_used
41
- def initialize(random_user_agent_used)
42
- @random_user_agent_used = random_user_agent_used
43
- end
40
+ # @param [ Boolean ] random_user_agent_used
41
+ def initialize(random_user_agent_used)
42
+ @random_user_agent_used = random_user_agent_used
43
+ end
44
44
 
45
- def to_s
46
- msg = if random_user_agent_used
47
- 'Well... --random-user-agent didn\'t work, you\'re on your own now!'
48
- else
49
- 'Please re-try with --random-user-agent'
50
- end
45
+ def to_s
46
+ msg = if random_user_agent_used
47
+ 'Well... --random-user-agent didn\'t work, you\'re on your own now!'
48
+ else
49
+ 'Please re-try with --random-user-agent'
50
+ end
51
51
 
52
- "The target is responding with a 403, this might be due to a WAF. #{msg}"
52
+ "The target is responding with a 403, this might be due to a WAF. #{msg}"
53
+ end
53
54
  end
54
- end
55
55
 
56
- # HTTP Redirect Error
57
- class HTTPRedirectError < Error
58
- attr_reader :redirect_uri
56
+ # HTTP Redirect Error
57
+ class HTTPRedirect < Standard
58
+ attr_reader :redirect_uri
59
59
 
60
- # @param [ String ] url
61
- def initialize(url)
62
- @redirect_uri = Addressable::URI.parse(url).normalize
63
- end
60
+ # @param [ String ] url
61
+ def initialize(url)
62
+ @redirect_uri = Addressable::URI.parse(url).normalize
63
+ end
64
64
 
65
- def to_s
66
- "The URL supplied redirects to #{redirect_uri}. Use the --ignore-main-redirect "\
67
- 'option to ignore the redirection and scan the target.'
65
+ def to_s
66
+ "The URL supplied redirects to #{redirect_uri}. Use the --ignore-main-redirect "\
67
+ 'option to ignore the redirection and scan the target.'
68
+ end
68
69
  end
69
70
  end
70
71
  end
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
- # Used instead of the Timeout::Error
3
- class MaxScanDurationReachedError < Error
4
- # :nocov:
5
- def to_s
6
- 'Max Scan Duration Reached'
4
+ module Error
5
+ # Used instead of the Timeout::Error
6
+ class MaxScanDurationReached < Standard
7
+ # :nocov:
8
+ def to_s
9
+ 'Max Scan Duration Reached'
10
+ end
11
+ # :nocov:
7
12
  end
8
- # :nocov:
9
13
  end
10
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  # Exit Code Values
3
5
  module ExitCode
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cms_scanner/finders/finder'
2
4
  require 'cms_scanner/finders/finding'
3
5
  require 'cms_scanner/finders/findings'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # Base class container for the Finders (i.e IndependentFinders etc)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cms_scanner/finders/finder/smart_url_checker'
2
4
  require 'cms_scanner/finders/finder/enumerator'
3
5
  require 'cms_scanner/finders/finder/fingerprinter'
@@ -8,7 +10,7 @@ module CMSScanner
8
10
  # Finder
9
11
  class Finder
10
12
  # Constants for common found_by
11
- DIRECT_ACCESS = 'Direct Access (Aggressive Detection)'.freeze
13
+ DIRECT_ACCESS = 'Direct Access (Aggressive Detection)'
12
14
 
13
15
  attr_accessor :target, :progress_bar
14
16
 
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  class Finder
4
6
  # Module to provide an easy way to perform password attacks
5
7
  module BreadthFirstDictionaryAttack
6
- # @param [ Array<CMSScanner::User> ] users
8
+ # @param [ Array<CMSScanner::Model::User> ] users
7
9
  # @param [ Array<String> ] passwords
8
10
  # @param [ Hash ] opts
9
11
  # @option opts [ Boolean ] :show_progression
@@ -1,30 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  class Finder
4
6
  # Module to provide an easy way to enumerate items such as plugins, themes etc
5
7
  module Enumerator
8
+ # @return [ Hash ]
9
+ def head_or_get_request_params
10
+ # Disabling the cache, as it causes a 'stack level too deep' exception
11
+ # with a large number of requests.
12
+ # See https://github.com/typhoeus/typhoeus/issues/408
13
+ @head_or_get_request_params ||= target.head_or_get_params.merge(cache_ttl: 0)
14
+ end
15
+
16
+ # @return [ Array<Integer> ]
17
+ def valid_response_codes
18
+ @valid_response_codes ||= [200]
19
+ end
20
+
6
21
  # @param [ Hash ] The target urls
7
22
  # @param [ Hash ] opts
8
23
  # @option opts [ Boolean ] :show_progression Wether or not to display the progress bar
9
24
  # @option opts [ Regexp ] :exclude_content
25
+ # @option opts [ Boolean, Array, String ] :check_full_response
10
26
  #
11
27
  # @yield [ Typhoeus::Response, String ]
12
- def enumerate(target_urls, opts = {})
13
- create_progress_bar(opts.merge(total: target_urls.size))
28
+ def enumerate(urls, opts = {})
29
+ create_progress_bar(opts.merge(total: urls.size))
14
30
 
15
- target_urls.each do |url, id|
16
- request = browser.forge_request(url, request_params)
31
+ urls.each do |url, id|
32
+ request = browser.forge_request(url, head_or_get_request_params)
17
33
 
18
- request.on_complete do |res|
34
+ request.on_complete do |head_res|
19
35
  progress_bar.increment
20
36
 
21
- next if target.homepage_or_404?(res)
37
+ next unless valid_response_codes.include?(head_res.code)
38
+
39
+ next if opts[:exclude_content] && head_res.response_headers&.match(opts[:exclude_content])
22
40
 
23
- if opts[:exclude_content]
24
- next if res.response_headers&.match(opts[:exclude_content]) || res.body.match(opts[:exclude_content])
25
- end
41
+ head_or_full_res = maybe_get_full_response(head_res, opts)
26
42
 
27
- yield res, id
43
+ yield head_or_full_res, id if head_or_full_res
28
44
  end
29
45
 
30
46
  hydra.queue(request)
@@ -33,12 +49,25 @@ module CMSScanner
33
49
  hydra.run
34
50
  end
35
51
 
52
+ # @param [ Typhoeus::Response ] head_res
53
+ # @param [ Hash ] opts
54
+ #
55
+ # @return [ Typhoeus::Response, nil ]
56
+ def maybe_get_full_response(head_res, opts)
57
+ return head_res unless opts[:check_full_response] == true ||
58
+ [*opts[:check_full_response]].include?(head_res.code)
59
+
60
+ full_res = NS::Browser.get(head_res.effective_url, full_request_params)
61
+
62
+ return if target.homepage_or_404?(full_res) ||
63
+ opts[:exclude_content] && full_res.body&.match(opts[:exclude_content])
64
+
65
+ full_res
66
+ end
67
+
36
68
  # @return [ Hash ]
37
- def request_params
38
- # disabling the cache, as it causes a 'stack level too deep' exception
39
- # with a large number of requests :/
40
- # See https://github.com/typhoeus/typhoeus/issues/408
41
- { cache_ttl: 0 }
69
+ def full_request_params
70
+ @full_request_params ||= {}
42
71
  end
43
72
  end
44
73
  end
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  class Finder
4
6
  # Module to provide an easy way to fingerprint things such as versions
5
7
  module Fingerprinter
8
+ include Enumerator
9
+
6
10
  # @param [ Hash ] fingerprints The fingerprints
7
11
  # Format should be like the following:
8
12
  # {
@@ -23,31 +27,15 @@ module CMSScanner
23
27
  # @yield [ Mixed, String, String ] version/s, url, hash The version associated to the
24
28
  # fingerprint of the url
25
29
  def fingerprint(fingerprints, opts = {})
26
- create_progress_bar(opts.merge(total: fingerprints.size))
27
-
28
- fingerprints.each do |path, f|
29
- url = target.url(path.dup)
30
- request = browser.forge_request(url, request_params)
31
-
32
- request.on_complete do |res|
33
- progress_bar.increment
30
+ enum_opts = opts.merge(check_full_response: 200)
34
31
 
35
- md5sum = hexdigest(res.body)
32
+ enumerate(fingerprints.transform_keys { |k| target.url(k) }, enum_opts) do |res, fingerprint|
33
+ md5sum = hexdigest(res.body)
36
34
 
37
- next unless f.key?(md5sum)
35
+ next unless fingerprint.key?(md5sum)
38
36
 
39
- yield f[md5sum], url, md5sum
40
- end
41
-
42
- hydra.queue(request)
37
+ yield fingerprint[md5sum], res.effective_url, md5sum
43
38
  end
44
-
45
- hydra.run
46
- end
47
-
48
- # @return [ Hash ]
49
- def request_params
50
- {}
51
39
  end
52
40
 
53
41
  # @return [ String ] The hashed value for the given body
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cms_scanner/finders/finder/smart_url_checker/findings'
2
4
 
3
5
  module CMSScanner
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  class Finder
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # Finding
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # Findings container
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # Independent Finder
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # This class is designed to handle independent results
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # Same Type Finder
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CMSScanner
2
4
  module Finders
3
5
  # This class is designed to handle same type results, such as enumeration of plugins,