cms_scanner 0.0.41.10 → 0.0.42.0

Sign up to get free protection for your applications and to get access to all the features.
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,