new_cms_scanner 0.13.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +26 -0
  4. data/app/app.rb +24 -0
  5. data/app/controllers/core/cli_options.rb +117 -0
  6. data/app/controllers/core.rb +82 -0
  7. data/app/controllers/interesting_findings.rb +25 -0
  8. data/app/finders/interesting_findings/fantastico_fileslist.rb +21 -0
  9. data/app/finders/interesting_findings/headers.rb +17 -0
  10. data/app/finders/interesting_findings/robots_txt.rb +20 -0
  11. data/app/finders/interesting_findings/search_replace_db_2.rb +19 -0
  12. data/app/finders/interesting_findings/xml_rpc.rb +61 -0
  13. data/app/finders/interesting_findings.rb +25 -0
  14. data/app/formatters/cli.rb +65 -0
  15. data/app/formatters/cli_no_color.rb +9 -0
  16. data/app/formatters/cli_no_colour.rb +17 -0
  17. data/app/formatters/json.rb +14 -0
  18. data/app/models/fantastico_fileslist.rb +34 -0
  19. data/app/models/headers.rb +44 -0
  20. data/app/models/interesting_finding.rb +48 -0
  21. data/app/models/robots_txt.rb +31 -0
  22. data/app/models/search_replace_db_2.rb +17 -0
  23. data/app/models/user.rb +35 -0
  24. data/app/models/version.rb +49 -0
  25. data/app/models/xml_rpc.rb +78 -0
  26. data/app/user_agents.txt +46 -0
  27. data/app/views/cli/core/banner.erb +1 -0
  28. data/app/views/cli/core/finished.erb +8 -0
  29. data/app/views/cli/core/help.erb +4 -0
  30. data/app/views/cli/core/started.erb +6 -0
  31. data/app/views/cli/core/version.erb +1 -0
  32. data/app/views/cli/interesting_findings/_array.erb +10 -0
  33. data/app/views/cli/interesting_findings/findings.erb +23 -0
  34. data/app/views/cli/scan_aborted.erb +5 -0
  35. data/app/views/cli/usage.erb +3 -0
  36. data/app/views/json/core/banner.erb +1 -0
  37. data/app/views/json/core/finished.erb +10 -0
  38. data/app/views/json/core/help.erb +4 -0
  39. data/app/views/json/core/started.erb +5 -0
  40. data/app/views/json/core/version.erb +1 -0
  41. data/app/views/json/interesting_findings/findings.erb +24 -0
  42. data/app/views/json/scan_aborted.erb +5 -0
  43. data/lib/cms_scanner/browser/actions.rb +48 -0
  44. data/lib/cms_scanner/browser/options.rb +90 -0
  45. data/lib/cms_scanner/browser.rb +96 -0
  46. data/lib/cms_scanner/cache/file_store.rb +77 -0
  47. data/lib/cms_scanner/cache/typhoeus.rb +25 -0
  48. data/lib/cms_scanner/controller.rb +105 -0
  49. data/lib/cms_scanner/controllers.rb +67 -0
  50. data/lib/cms_scanner/errors/http.rb +72 -0
  51. data/lib/cms_scanner/errors/scan.rb +14 -0
  52. data/lib/cms_scanner/errors.rb +11 -0
  53. data/lib/cms_scanner/exit_code.rb +25 -0
  54. data/lib/cms_scanner/finders/base_finders.rb +45 -0
  55. data/lib/cms_scanner/finders/finder/breadth_first_dictionary_attack.rb +121 -0
  56. data/lib/cms_scanner/finders/finder/enumerator.rb +77 -0
  57. data/lib/cms_scanner/finders/finder/fingerprinter.rb +48 -0
  58. data/lib/cms_scanner/finders/finder/smart_url_checker/findings.rb +33 -0
  59. data/lib/cms_scanner/finders/finder/smart_url_checker.rb +60 -0
  60. data/lib/cms_scanner/finders/finder.rb +75 -0
  61. data/lib/cms_scanner/finders/finding.rb +54 -0
  62. data/lib/cms_scanner/finders/findings.rb +26 -0
  63. data/lib/cms_scanner/finders/independent_finder.rb +30 -0
  64. data/lib/cms_scanner/finders/independent_finders.rb +26 -0
  65. data/lib/cms_scanner/finders/same_type_finder.rb +19 -0
  66. data/lib/cms_scanner/finders/same_type_finders.rb +26 -0
  67. data/lib/cms_scanner/finders/unique_finder.rb +19 -0
  68. data/lib/cms_scanner/finders/unique_finders.rb +47 -0
  69. data/lib/cms_scanner/finders.rb +12 -0
  70. data/lib/cms_scanner/formatter/buffer.rb +17 -0
  71. data/lib/cms_scanner/formatter.rb +149 -0
  72. data/lib/cms_scanner/helper.rb +7 -0
  73. data/lib/cms_scanner/numeric.rb +13 -0
  74. data/lib/cms_scanner/parsed_cli.rb +37 -0
  75. data/lib/cms_scanner/progressbar_null_output.rb +23 -0
  76. data/lib/cms_scanner/public_suffix/domain.rb +42 -0
  77. data/lib/cms_scanner/references.rb +132 -0
  78. data/lib/cms_scanner/scan.rb +88 -0
  79. data/lib/cms_scanner/target/hashes.rb +45 -0
  80. data/lib/cms_scanner/target/platform/php.rb +62 -0
  81. data/lib/cms_scanner/target/platform.rb +3 -0
  82. data/lib/cms_scanner/target/scope.rb +103 -0
  83. data/lib/cms_scanner/target/server/apache.rb +27 -0
  84. data/lib/cms_scanner/target/server/generic.rb +72 -0
  85. data/lib/cms_scanner/target/server/iis.rb +29 -0
  86. data/lib/cms_scanner/target/server/nginx.rb +27 -0
  87. data/lib/cms_scanner/target/server.rb +6 -0
  88. data/lib/cms_scanner/target.rb +124 -0
  89. data/lib/cms_scanner/typhoeus/hydra.rb +12 -0
  90. data/lib/cms_scanner/typhoeus/response.rb +27 -0
  91. data/lib/cms_scanner/version.rb +6 -0
  92. data/lib/cms_scanner/vulnerability.rb +46 -0
  93. data/lib/cms_scanner/web_site.rb +145 -0
  94. data/lib/cms_scanner.rb +141 -0
  95. metadata +426 -0
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # Interesting Finding
6
+ class InterestingFinding
7
+ include Finders::Finding
8
+
9
+ attr_reader :url
10
+ attr_writer :to_s
11
+
12
+ # @param [ String ] url
13
+ # @param [ Hash ] opts
14
+ # :to_s (override the to_s method)
15
+ # See Finders::Finding for other available options
16
+ def initialize(url, opts = {})
17
+ @url = url
18
+ @to_s = opts[:to_s]
19
+
20
+ parse_finding_options(opts)
21
+ end
22
+
23
+ # @return [ Array<String> ]
24
+ def entries
25
+ res = NS::Browser.get(url)
26
+
27
+ return [] unless res && res.headers['Content-Type'] =~ %r{\Atext/plain;}i
28
+
29
+ res.body.split("\n").reject { |s| s.strip.empty? }
30
+ end
31
+
32
+ # @return [ String ]
33
+ def to_s
34
+ @to_s || url
35
+ end
36
+
37
+ # @return [ String ]
38
+ def type
39
+ @type ||= self.class.to_s.demodulize.underscore
40
+ end
41
+
42
+ # @return [ Boolean ]
43
+ def ==(other)
44
+ self.class == other.class && to_s == other.to_s
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # Robots.txt
6
+ class RobotsTxt < InterestingFinding
7
+ # @return [ String ]
8
+ def to_s
9
+ @to_s ||= "robots.txt found: #{url}"
10
+ end
11
+
12
+ # @todo Better detection, currently everything not empty or / is returned
13
+ #
14
+ # @return [ Array<String> ] The interesting Allow/Disallow rules detected
15
+ def interesting_entries
16
+ results = []
17
+
18
+ entries.each do |entry|
19
+ next unless entry =~ /\A(?:dis)?allow:\s*(.+)\z/i
20
+
21
+ match = Regexp.last_match(1)
22
+ next if match == '/'
23
+
24
+ results << match
25
+ end
26
+
27
+ results.uniq
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # SearchReplaceDB2
6
+ class SearchReplaceDB2 < InterestingFinding
7
+ # @return [ String ]
8
+ def to_s
9
+ @to_s ||= "Search Replace DB script found: #{url}"
10
+ end
11
+
12
+ def references
13
+ @references ||= { url: ['https://interconnectit.com/products/search-and-replace-for-wordpress-databases/'] }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # User
6
+ class User
7
+ include Finders::Finding
8
+
9
+ attr_accessor :password
10
+ attr_reader :id, :username
11
+
12
+ # @param [ String ] username
13
+ # @param [ Hash ] opts
14
+ # @option opts [ Integer ] :id
15
+ # @option opts [ String ] :password
16
+ def initialize(username, opts = {})
17
+ @username = username
18
+ @password = opts[:password]
19
+ @id = opts[:id]
20
+
21
+ parse_finding_options(opts)
22
+ end
23
+
24
+ def ==(other)
25
+ return false unless self.class == other.class
26
+
27
+ username == other.username && password == other.password
28
+ end
29
+
30
+ def to_s
31
+ username
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # Version
6
+ class Version
7
+ include Finders::Finding
8
+
9
+ attr_reader :number
10
+
11
+ def initialize(number, opts = {})
12
+ @number = number.to_s
13
+ @number = "0#{number}" if @number[0, 1] == '.'
14
+
15
+ parse_finding_options(opts)
16
+ end
17
+
18
+ # @param [ Version, String ] other
19
+ # rubocop:disable Style/NumericPredicate
20
+ def ==(other)
21
+ (self <=> other) == 0
22
+ end
23
+ # rubocop:enable all
24
+
25
+ # @param [ Version, String ] other
26
+ def <(other)
27
+ (self <=> other) == -1
28
+ end
29
+
30
+ # @param [ Version, String ] other
31
+ def >(other)
32
+ (self <=> other) == 1
33
+ end
34
+
35
+ # @param [ Version, String ] other
36
+ def <=>(other)
37
+ other = self.class.new(other) unless other.is_a?(self.class) # handle potential '.1' version
38
+
39
+ Gem::Version.new(number) <=> Gem::Version.new(other.number)
40
+ rescue ArgumentError
41
+ false
42
+ end
43
+
44
+ def to_s
45
+ number
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # XML RPC
6
+ class XMLRPC < InterestingFinding
7
+ # @return [ String ]
8
+ def to_s
9
+ @to_s ||= "XML-RPC seems to be enabled: #{url}"
10
+ end
11
+
12
+ # @return [ Browser ]
13
+ def browser
14
+ @browser ||= NS::Browser.instance
15
+ end
16
+
17
+ # @return [ Array<String> ]
18
+ def available_methods
19
+ return @available_methods if @available_methods
20
+
21
+ @available_methods = []
22
+
23
+ res = method_call('system.listMethods').run
24
+ doc = Nokogiri::XML.parse(res.body)
25
+
26
+ doc.search('methodResponse params param value array data value string').each do |s|
27
+ @available_methods << s.text
28
+ end
29
+
30
+ @available_methods
31
+ end
32
+
33
+ # @return [ Boolean ] Whether or not the XMLRPC is enabled
34
+ def enabled?
35
+ !available_methods.empty?
36
+ end
37
+
38
+ # @param [ String ] method_name
39
+ # @param [ Array ] method_params
40
+ # @param [ Hash ] request_params
41
+ #
42
+ # @return [ Typhoeus::Request ]
43
+ def method_call(method_name, method_params = [], request_params = {})
44
+ browser.forge_request(
45
+ url,
46
+ request_params.merge(
47
+ method: :post,
48
+ body: ::XMLRPC::Create.new.methodCall(method_name, *method_params)
49
+ )
50
+ )
51
+ end
52
+
53
+ # @param [ Array<Array> ] methods_and_params
54
+ # @param [ Hash ] request_params
55
+ #
56
+ # Example of methods_and_params:
57
+ # [
58
+ # [method1, param1, param2],
59
+ # [method2, param1],
60
+ # [method3]
61
+ # ]
62
+ #
63
+ # @return [ Typhoeus::Request ]
64
+ def multi_call(methods_and_params = [], request_params = {})
65
+ browser.forge_request(
66
+ url,
67
+ request_params.merge(
68
+ method: :post,
69
+ body: ::XMLRPC::Create.new.methodCall(
70
+ 'system.multicall',
71
+ methods_and_params.collect { |m| { methodName: m[0], params: m[1..-1] } }
72
+ )
73
+ )
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,46 @@
1
+ # Windows
2
+ Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5
3
+ Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14
4
+ Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.27 (KHTML, like Gecko) Chrome/12.0.712.0 Safari/534.27
5
+ Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1
6
+ Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0E)
7
+ Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
8
+ Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
9
+ Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
10
+ Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6
11
+ Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1
12
+ Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0
13
+ Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1
14
+ Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)
15
+ Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
16
+ Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0)
17
+ Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00
18
+ Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5
19
+
20
+ # MAC
21
+ Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13
22
+ Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15
23
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
24
+ Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3
25
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3
26
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1
27
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10
28
+
29
+ # Linux
30
+ Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1
31
+ Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24
32
+ Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100915 Gentoo Firefox/3.6.9
33
+ Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.16) Gecko/20120421 Gecko Firefox/11.0
34
+ Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0
35
+ Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00
36
+ Mozilla/5.0 (X11; U; Linux x86_64; us; rv:1.9.1.19) Gecko/20110430 shadowfox/7.0 (like Firefox/7.0
37
+
38
+ # iPad
39
+ Mozilla/5.0 (iPad; CPU OS 7_1_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D201 Safari/9537.53
40
+ Mozilla/5.0 (iPad; CPU OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53
41
+ Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25
42
+
43
+ # iPhone
44
+ Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53
45
+ Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B511 Safari/9537.53
46
+ Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D201 Safari/9537.53
@@ -0,0 +1 @@
1
+ <% # Empty file, the banner should be implemented in each scanner %>
@@ -0,0 +1,8 @@
1
+
2
+ <%= info_icon %> Finished: <%= @stop_time.asctime %>
3
+ <%= info_icon %> Requests Done: <%= @requests_done %>
4
+ <%= info_icon %> Cached Requests: <%= @cached_requests %>
5
+ <%= info_icon %> Data Sent: <%= @data_sent.bytes_to_human %>
6
+ <%= info_icon %> Data Received: <%= @data_received.bytes_to_human %>
7
+ <%= info_icon %> Memory used: <%= @used_memory.bytes_to_human %>
8
+ <%= info_icon %> Elapsed time: <%= Time.at(@elapsed).utc.strftime('%H:%M:%S') %>
@@ -0,0 +1,4 @@
1
+ <%= @help %>
2
+ <% if @simple -%>
3
+ [!] To see full list of options use --hh.
4
+ <% end -%>
@@ -0,0 +1,6 @@
1
+ <%= info_icon %> URL: <%= @url %> [<%= @ip %>]
2
+ <% if @url != @effective_url -%>
3
+ <%= info_icon %> Effective URL: <%= @effective_url %>
4
+ <% end -%>
5
+ <%= info_icon %> Started: <%= @start_time.asctime %>
6
+
@@ -0,0 +1 @@
1
+ Version: <%= NS::VERSION %>
@@ -0,0 +1,10 @@
1
+ <% unless @a.empty? -%>
2
+ <% if @a.size == 1 -%>
3
+ | <%= @s %>: <%= @a.first %>
4
+ <% else -%>
5
+ | <%= @p %>:
6
+ <% @a.each do |line| -%>
7
+ | - <%= line %>
8
+ <% end -%>
9
+ <% end -%>
10
+ <% end -%>
@@ -0,0 +1,23 @@
1
+ <% unless @findings.empty? -%>
2
+ Interesting Finding(s):
3
+ <% @findings.each do |finding| -%>
4
+
5
+ <%= info_icon %> <%= finding %>
6
+ <%= render('_array', a: finding.interesting_entries, s: 'Interesting Entry', p: 'Interesting Entries') -%>
7
+ | Found By: <%= finding.found_by %>
8
+ <% if finding.confidence > 0 -%>
9
+ | Confidence: <%= finding.confidence %>%
10
+ <% end -%>
11
+ <% unless (confirmed = finding.confirmed_by).empty? -%>
12
+ <% if confirmed.size == 1 -%>
13
+ | Confirmed By: <%= confirmed.first.found_by %><% if confirmed.first.confidence > 0 %>, <%= confirmed.first.confidence %>% confidence<% end %>
14
+ <% else -%>
15
+ | Confirmed By:
16
+ <% confirmed.each do |c| -%>
17
+ | - <%= c.found_by %><% if c.confidence > 0 %>, <%= c.confidence %>% confidence<% end %>
18
+ <% end -%>
19
+ <% end -%>
20
+ <% end -%>
21
+ <%= render('_array', a: finding.references_urls, s: 'Reference', p: 'References') -%>
22
+ <% end -%>
23
+ <% end %>
@@ -0,0 +1,5 @@
1
+
2
+ Scan Aborted: <%= @reason %>
3
+ <% if @verbose -%>
4
+ Trace: <%= @trace.join("\n") %>
5
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= @msg %>
2
+
3
+ Please use --help/-h for the list of available options.
@@ -0,0 +1 @@
1
+ <% # Empty file, the banner should be implemented in each scanner %>
@@ -0,0 +1,10 @@
1
+ "stop_time": <%= @stop_time.to_i %>,
2
+ "elapsed": <%= @elapsed.to_i %>,
3
+ "requests_done": <%= @requests_done.to_i %>,
4
+ "cached_requests": <%= @cached_requests.to_i %>,
5
+ "data_sent": <%= @data_sent.to_i %>,
6
+ "data_sent_humanised": <%= @data_sent.bytes_to_human.to_json %>,
7
+ "data_received": <%= @data_received.to_i %>,
8
+ "data_received_humanised": <%= @data_received.bytes_to_human.to_json %>,
9
+ "used_memory": <%= @used_memory.to_i %>,
10
+ "used_memory_humanised": <%= @used_memory.bytes_to_human.to_json %>,
@@ -0,0 +1,4 @@
1
+ "help": <%= @help.to_s.to_json %>,
2
+ <% if @simple -%>
3
+ "full_help": "To see full list of options use --hh.",
4
+ <% end -%>
@@ -0,0 +1,5 @@
1
+ "start_time": <%= @start_time.to_i %>,
2
+ "start_memory": <%= @start_memory.to_i %>,
3
+ "target_url": <%= @url.to_s.to_json %>,
4
+ "target_ip": <%= @ip.to_s.to_json %>,
5
+ "effective_url": <%= @effective_url.to_s.to_json %>,
@@ -0,0 +1 @@
1
+ "version": <%= NS::VERSION.to_s.to_json %>,
@@ -0,0 +1,24 @@
1
+ "interesting_findings": [
2
+ <% unless @findings.empty? -%>
3
+ <% last_index = @findings.size - 1 %>
4
+ <% @findings.each.with_index do |finding, index| -%>
5
+ {
6
+ "url": <%= finding.url.to_s.to_json %>,
7
+ "to_s": <%= finding.to_s.to_json %>,
8
+ "type": <%= finding.type.to_json %>,
9
+ "found_by": <%= finding.found_by.to_s.to_json %>,
10
+ "confidence": <%= finding.confidence.to_json %>,
11
+ "confirmed_by": {
12
+ <% unless (confirmed = finding.confirmed_by).empty? -%>
13
+ <% c_last_index = confirmed.size - 1 %>
14
+ <% confirmed.each.with_index do |c, i| -%>
15
+ <%= c.found_by.to_s.to_json %>: { "confidence": <%= c.confidence.to_json %> }<% unless i == c_last_index %>,<% end %>
16
+ <% end -%>
17
+ <% end -%>
18
+ },
19
+ "references": <%= finding.references.to_json %>,
20
+ "interesting_entries": <%= finding.interesting_entries.to_json %>
21
+ }<% unless index == last_index %>,<% end %>
22
+ <% end -%>
23
+ <% end -%>
24
+ ],
@@ -0,0 +1,5 @@
1
+ "scan_aborted": <%= @reason.to_json %>,
2
+ "target_url": <%= @url.to_json %>,
3
+ <% if @verbose -%>
4
+ "trace": <%= @trace.to_json %>,
5
+ <% end %>
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ class Browser
5
+ # Browser Actions (get, post etc)
6
+ module Actions
7
+ # @param [ String ] url
8
+ # @param [ Hash ] params
9
+ #
10
+ # @return [ Typhoeus::Request ]
11
+ def forge_request(url, params = {})
12
+ NS::Browser.instance.forge_request(url, params)
13
+ end
14
+
15
+ # @param [ String ] url
16
+ # @param [ Hash ] params
17
+ #
18
+ # @return [ Typhoeus::Response ]
19
+ def get(url, params = {})
20
+ forge_request(url, params.merge(method: :get)).run
21
+ end
22
+
23
+ # @param [ String ] url
24
+ # @param [ Hash ] params
25
+ #
26
+ # @return [ Typhoeus::Response ]
27
+ def post(url, params = {})
28
+ forge_request(url, params.merge(method: :post)).run
29
+ end
30
+
31
+ # @param [ String ] url
32
+ # @param [ Hash ] params
33
+ #
34
+ # @return [ Typhoeus::Response ]
35
+ def head(url, params = {})
36
+ forge_request(url, params.merge(method: :head)).run
37
+ end
38
+
39
+ # @param [ String ] url
40
+ # @param [ Hash ] params
41
+ #
42
+ # @return [ Typhoeus::Response ]
43
+ def get_and_follow_location(url, params = {})
44
+ get(url, { followlocation: true, maxredirs: 3 }.merge(params))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ # Options available in the Browser
5
+ class Browser
6
+ OPTIONS = %i[
7
+ cache_ttl
8
+ cookie_jar
9
+ cookie_string
10
+ connect_timeout
11
+ disable_tls_checks
12
+ headers
13
+ http_auth
14
+ max_threads
15
+ proxy
16
+ proxy_auth
17
+ random_user_agent
18
+ request_timeout
19
+ throttle
20
+ url
21
+ user_agent
22
+ user_agents_list
23
+ vhost
24
+ ].freeze
25
+
26
+ attr_accessor(*OPTIONS)
27
+
28
+ # @return [ String ]
29
+ def default_user_agent
30
+ "#{NS} v#{NS::VERSION}"
31
+ end
32
+
33
+ # @return [ Typhoeus::Hydra ]
34
+ def hydra
35
+ @hydra ||= Typhoeus::Hydra.new(max_concurrency: max_threads || 1)
36
+ end
37
+
38
+ # @param [ Hash ] options
39
+ def load_options(options = {})
40
+ OPTIONS.each do |sym|
41
+ send("#{sym}=", options[sym]) if options.key?(sym)
42
+ end
43
+ end
44
+
45
+ # Set the threads attribute and update hydra accordinly
46
+ # If the throttle attribute is > 0, max_threads will be forced to 1
47
+ #
48
+ # @param [ Integer ] number
49
+ def max_threads=(number)
50
+ @max_threads = number.to_i.positive? && throttle.zero? ? number.to_i : 1
51
+
52
+ hydra.max_concurrency = @max_threads
53
+ end
54
+
55
+ # @return [ String ] The user agent
56
+ def user_agent
57
+ @user_agent ||= random_user_agent ? user_agents.sample : default_user_agent
58
+ end
59
+
60
+ # @return [ Array<String> ]
61
+ def user_agents
62
+ return @user_agents if @user_agents
63
+
64
+ @user_agents = []
65
+
66
+ # The user_agents_list is managed by the CLI options, with the default being
67
+ # APP_DIR/user_agents.txt
68
+ File.open(user_agents_list).each do |line|
69
+ next if line == "\n" || line[0, 1] == '#'
70
+
71
+ @user_agents << line.chomp
72
+ end
73
+
74
+ @user_agents
75
+ end
76
+
77
+ # @param [ value ] The throttle time in milliseconds
78
+ #
79
+ # if value > 0, the max_threads will be set to 1
80
+ def throttle=(value)
81
+ @throttle = value.to_i.abs / 1000.0
82
+
83
+ self.max_threads = 1 if @throttle.positive?
84
+ end
85
+
86
+ def trottle!
87
+ sleep(throttle) if throttle.positive?
88
+ end
89
+ end
90
+ end