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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 56edd064a963293e585d678c18e58db39cbd514a56586ab7f445bc9f28d4d10b
4
+ data.tar.gz: 52a249bb09f8b840e67bce8e51f1f2170a1a4fc9efaf3ab9726417bb4b6ca83c
5
+ SHA512:
6
+ metadata.gz: e1c015727d38e5019eeff523c3951e0b8291968b9c437c986fd483d73d012aece1bc8c92e79d3a048a17389c2d62532d8d0039b0833b1ac30b93f449b26acf09
7
+ data.tar.gz: 237d542e023caf48867ed491e53c51b5a63a3fbffaeb043405644c6346462ebd3b11860fc2d1cec151ae76a5c89d67d012b12c1c1adb540e8ae9078096254674
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2014-2015 - WPScanTeam
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # CMSScanner
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/cms_scanner.svg)](https://badge.fury.io/rb/cms_scanner)
4
+ ![Build](https://github.com/wpscanteam/CMSScanner/workflows/Build/badge.svg)
5
+ [![Coverage Status](https://img.shields.io/coveralls/wpscanteam/CMSScanner.svg)](https://coveralls.io/r/wpscanteam/CMSScanner)
6
+ [![Code Climate](https://api.codeclimate.com/v1/badges/b90b7f9f6982792ef8d6/maintainability)](https://codeclimate.com/github/wpscanteam/CMSScanner/maintainability)
7
+
8
+ The goal of this gem is to provide a quick and easy way to create a CMS/WebSite Scanner by acting like a Framework and providing classes, formatters etc.
9
+
10
+ ## /!\ This gem is currently Experimental /!\
11
+
12
+ ## A basic implementation example is available in the example folder.
13
+
14
+ To start to play with it, copy all its files and folders into a new git repository and run `bundle install && rake install` inside it.
15
+ It will create a `cmsscan` command that you can run against a target, ie `cmsscan --url https://www.google.com`
16
+
17
+
18
+ Install Dependencies: `bundle install`
19
+
20
+ ## Contributing
21
+
22
+ 1. Fork it ( https://github.com/wpscanteam/CMSScanner/fork )
23
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
24
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
25
+ 4. Push to the branch (`git push origin my-new-feature`)
26
+ 5. Create new Pull Request
data/app/app.rb ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Formatters
4
+ require_relative 'formatters/cli'
5
+ require_relative 'formatters/cli_no_colour'
6
+ require_relative 'formatters/cli_no_color'
7
+ require_relative 'formatters/json'
8
+
9
+ # Controllers
10
+ require_relative 'controllers/core'
11
+ require_relative 'controllers/interesting_findings'
12
+
13
+ # Models
14
+ require_relative 'models/interesting_finding'
15
+ require_relative 'models/robots_txt'
16
+ require_relative 'models/fantastico_fileslist'
17
+ require_relative 'models/search_replace_db_2'
18
+ require_relative 'models/headers'
19
+ require_relative 'models/xml_rpc'
20
+ require_relative 'models/version'
21
+ require_relative 'models/user'
22
+
23
+ # Finders
24
+ require_relative 'finders/interesting_findings'
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Controller
5
+ # CLI Options for the Core Controller
6
+ class Core < Base
7
+ def cli_options
8
+ formats = NS::Formatter.availables
9
+
10
+ [
11
+ OptURL.new(['-u', '--url URL', 'The URL to scan'],
12
+ required_unless: %i[help hh version],
13
+ default_protocol: 'http'),
14
+ OptBoolean.new(['--force', 'Do not check if target returns a 403'])
15
+ ] + mixed_cli_options + [
16
+ OptFilePath.new(['-o', '--output FILE', 'Output to FILE'], writable: true, exists: false),
17
+ OptChoice.new(['-f', '--format FORMAT',
18
+ 'Output results in the format supplied'], choices: formats),
19
+ OptChoice.new(['--detection-mode MODE'],
20
+ choices: %w[mixed passive aggressive],
21
+ normalize: :to_sym,
22
+ default: :mixed),
23
+ OptArray.new(['--scope DOMAINS',
24
+ 'Comma separated (sub-)domains to consider in scope. ',
25
+ 'Wildcard(s) allowed in the trd of valid domains, e.g: *.target.tld'], advanced: true)
26
+ ] + cli_browser_options
27
+ end
28
+
29
+ def mixed_cli_options
30
+ [
31
+ OptBoolean.new(['-h', '--help', 'Display the simple help and exit']),
32
+ OptBoolean.new(['--hh', 'Display the full help and exit']),
33
+ OptBoolean.new(['--version', 'Display the version and exit']),
34
+ OptBoolean.new(['--ignore-main-redirect', 'Ignore the main redirect (if any) and scan the target url'],
35
+ advanced: true),
36
+ OptBoolean.new(['-v', '--verbose', 'Verbose mode']),
37
+ OptBoolean.new(['--[no-]banner', 'Whether or not to display the banner'], default: true),
38
+ OptPositiveInteger.new(['--max-scan-duration SECONDS',
39
+ 'Abort the scan if it exceeds the time provided in seconds'],
40
+ advanced: true)
41
+ ]
42
+ end
43
+
44
+ # @return [ Array<OptParseValidator::OptBase> ]
45
+ def cli_browser_options
46
+ cli_browser_headers_options + [
47
+ OptBoolean.new(['--random-user-agent', '--rua',
48
+ 'Use a random user-agent for each scan']),
49
+ OptFilePath.new(['--user-agents-list FILE-PATH',
50
+ 'List of agents to use with --random-user-agent'],
51
+ exists: true,
52
+ advanced: true,
53
+ default: APP_DIR.join('user_agents.txt')),
54
+ OptCredentials.new(['--http-auth login:password']),
55
+ OptPositiveInteger.new(['-t', '--max-threads VALUE', 'The max threads to use'],
56
+ default: 5),
57
+ OptPositiveInteger.new(['--throttle MilliSeconds', 'Milliseconds to wait before doing another web request. ' \
58
+ 'If used, the max threads will be set to 1.']),
59
+ OptPositiveInteger.new(['--request-timeout SECONDS', 'The request timeout in seconds'],
60
+ default: 60),
61
+ OptPositiveInteger.new(['--connect-timeout SECONDS', 'The connection timeout in seconds'],
62
+ default: 30),
63
+ OptBoolean.new(['--disable-tls-checks',
64
+ 'Disables SSL/TLS certificate verification, and downgrade to TLS1.0+ ' \
65
+ '(requires cURL 7.66 for the latter)'])
66
+ ] + cli_browser_proxy_options + cli_browser_cookies_options + cli_browser_cache_options
67
+ end
68
+
69
+ # @return [ Array<OptParseValidator::OptBase> ]
70
+ def cli_browser_headers_options
71
+ [
72
+ OptString.new(['--user-agent VALUE', '--ua']),
73
+ OptHeaders.new(['--headers HEADERS', 'Additional headers to append in requests'], advanced: true),
74
+ OptString.new(['--vhost VALUE', 'The virtual host (Host header) to use in requests'], advanced: true)
75
+ ]
76
+ end
77
+
78
+ # @return [ Array<OptParseValidator::OptBase> ]
79
+ def cli_browser_proxy_options
80
+ [
81
+ OptProxy.new(['--proxy protocol://IP:port',
82
+ 'Supported protocols depend on the cURL installed']),
83
+ OptCredentials.new(['--proxy-auth login:password'])
84
+ ]
85
+ end
86
+
87
+ # @return [ Array<OptParseValidator::OptBase> ]
88
+ def cli_browser_cookies_options
89
+ [
90
+ OptString.new(['--cookie-string COOKIE',
91
+ 'Cookie string to use in requests, ' \
92
+ 'format: cookie1=value1[; cookie2=value2]']),
93
+ OptFilePath.new(['--cookie-jar FILE-PATH', 'File to read and write cookies'],
94
+ writable: true,
95
+ readable: true,
96
+ create: true,
97
+ default: File.join(tmp_directory, 'cookie_jar.txt'))
98
+ ]
99
+ end
100
+
101
+ # @return [ Array<OptParseValidator::OptBase> ]
102
+ def cli_browser_cache_options
103
+ [
104
+ OptInteger.new(['--cache-ttl TIME_TO_LIVE', 'The cache time to live in seconds'],
105
+ default: 600, advanced: true),
106
+ OptBoolean.new(['--clear-cache', 'Clear the cache before the scan'], advanced: true),
107
+ OptDirectoryPath.new(['--cache-dir PATH'],
108
+ readable: true,
109
+ writable: true,
110
+ create: true,
111
+ default: File.join(tmp_directory, 'cache'),
112
+ advanced: true)
113
+ ]
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/cli_options'
4
+
5
+ module CMSScanner
6
+ module Controller
7
+ # Core Controller
8
+ class Core < Base
9
+ def setup_cache
10
+ return unless NS::ParsedCli.cache_dir
11
+
12
+ storage_path = File.join(NS::ParsedCli.cache_dir, Digest::MD5.hexdigest(target.url))
13
+
14
+ Typhoeus::Config.cache = Cache::Typhoeus.new(storage_path)
15
+ Typhoeus::Config.cache.clean if NS::ParsedCli.clear_cache
16
+ end
17
+
18
+ def before_scan
19
+ maybe_output_banner_help_and_version
20
+
21
+ setup_cache
22
+ check_target_availability
23
+ end
24
+
25
+ def maybe_output_banner_help_and_version
26
+ output('banner') if NS::ParsedCli.banner
27
+ output('help', help: option_parser.simple_help, simple: true) if NS::ParsedCli.help
28
+ output('help', help: option_parser.full_help, simple: false) if NS::ParsedCli.hh
29
+ output('version') if NS::ParsedCli.version
30
+
31
+ exit(NS::ExitCode::OK) if NS::ParsedCli.help || NS::ParsedCli.hh || NS::ParsedCli.version
32
+ end
33
+
34
+ # Checks that the target is accessible, raises related errors otherwise
35
+ #
36
+ # @return [ Void ]
37
+ def check_target_availability
38
+ res = NS::Browser.get(target.url)
39
+
40
+ case res.code
41
+ when 0
42
+ raise Error::TargetDown, res
43
+ when 401
44
+ raise Error::HTTPAuthRequired
45
+ when 403
46
+ raise Error::AccessForbidden, NS::ParsedCli.random_user_agent unless NS::ParsedCli.force
47
+ when 407
48
+ raise Error::ProxyAuthRequired
49
+ end
50
+
51
+ # Checks for redirects
52
+ # An out of scope redirect will raise an Error::HTTPRedirect
53
+ effective_url = target.homepage_res.effective_url
54
+
55
+ return if target.in_scope?(effective_url)
56
+
57
+ raise Error::HTTPRedirect, effective_url unless NS::ParsedCli.ignore_main_redirect
58
+
59
+ target.homepage_res = res
60
+ end
61
+
62
+ def run
63
+ @start_time = Time.now
64
+ @start_memory = NS.start_memory
65
+
66
+ output('started', url: target.url, ip: target.ip, effective_url: target.homepage_url)
67
+ end
68
+
69
+ def after_scan
70
+ @stop_time = Time.now
71
+ @elapsed = @stop_time - @start_time
72
+ @used_memory = GetProcessMem.new.bytes - @start_memory
73
+
74
+ output('finished',
75
+ cached_requests: NS.cached_requests,
76
+ requests_done: NS.total_requests,
77
+ data_sent: NS.total_data_sent,
78
+ data_received: NS.total_data_received)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Controller
5
+ # InterestingFindings Controller
6
+ class InterestingFindings < Base
7
+ def cli_options
8
+ [
9
+ OptChoice.new(
10
+ ['--interesting-findings-detection MODE',
11
+ 'Use the supplied mode for the interesting findings detection. '],
12
+ choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
13
+ )
14
+ ]
15
+ end
16
+
17
+ def run
18
+ mode = NS::ParsedCli.interesting_findings_detection || NS::ParsedCli.detection_mode
19
+ findings = target.interesting_findings(mode: mode)
20
+
21
+ output('findings', findings: findings) unless findings.empty?
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Finders
5
+ module InterestingFindings
6
+ # FantasticoFileslist finder
7
+ class FantasticoFileslist < Finder
8
+ # @return [ InterestingFinding ]
9
+ def aggressive(_opts = {})
10
+ path = 'fantastico_fileslist.txt'
11
+ res = target.head_and_get(path)
12
+
13
+ return if res.body.strip.empty?
14
+ return unless res.headers && res.headers['Content-Type']&.start_with?('text/plain')
15
+
16
+ NS::Model::FantasticoFileslist.new(target.url(path), confidence: 70, found_by: found_by)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Finders
5
+ module InterestingFindings
6
+ # Interesting Headers finder
7
+ class Headers < Finder
8
+ # @return [ InterestingFinding ]
9
+ def passive(_opts = {})
10
+ r = NS::Model::Headers.new(target.homepage_url, confidence: 100, found_by: found_by)
11
+
12
+ r.interesting_entries.empty? ? nil : r
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Finders
5
+ module InterestingFindings
6
+ # Robots.txt finder
7
+ class RobotsTxt < Finder
8
+ # @return [ InterestingFinding ]
9
+ def aggressive(_opts = {})
10
+ path = 'robots.txt'
11
+ res = target.head_and_get(path)
12
+
13
+ return unless res&.code == 200 && res.body =~ /(?:user-agent|(?:dis)?allow):/i
14
+
15
+ NS::Model::RobotsTxt.new(target.url(path), confidence: 100, found_by: found_by)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Finders
5
+ module InterestingFindings
6
+ # SearchReplaceDB2 finder
7
+ class SearchReplaceDB2 < Finder
8
+ # @return [ InterestingFinding ]
9
+ def aggressive(_opts = {})
10
+ path = 'searchreplacedb2.php'
11
+
12
+ return unless /by interconnect/i.match?(target.head_and_get(path).body)
13
+
14
+ NS::Model::SearchReplaceDB2.new(target.url(path), confidence: 100, found_by: found_by)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Finders
5
+ module InterestingFindings
6
+ # XML RPC finder
7
+ class XMLRPC < Finder
8
+ # @return [ Array<String> ] The potential urls to the XMl RPC file
9
+ def potential_urls
10
+ @potential_urls ||= []
11
+ end
12
+
13
+ # @return [ Array<XMLRPC> ]
14
+ def passive(opts = {})
15
+ [passive_headers(opts), passive_body(opts)].compact
16
+ end
17
+
18
+ # @return [ XMLRPC ]
19
+ def passive_headers(_opts = {})
20
+ url = target.homepage_res.headers['X-Pingback']
21
+
22
+ return unless target.in_scope?(url)
23
+
24
+ potential_urls << url
25
+
26
+ NS::Model::XMLRPC.new(url, confidence: 30, found_by: 'Headers (Passive Detection)')
27
+ end
28
+
29
+ # @return [ XMLRPC ]
30
+ def passive_body(_opts = {})
31
+ target.homepage_res.html.css('link[rel="pingback"]').each do |tag|
32
+ url = tag.attribute('href').to_s
33
+
34
+ next unless target.in_scope?(url)
35
+
36
+ potential_urls << url
37
+
38
+ return NS::Model::XMLRPC.new(url, confidence: 30, found_by: 'Link Tag (Passive Detection)')
39
+ end
40
+ nil
41
+ end
42
+
43
+ # @return [ XMLRPC ]
44
+ def aggressive(_opts = {})
45
+ potential_urls << target.url('xmlrpc.php')
46
+
47
+ potential_urls.uniq.each do |potential_url|
48
+ next unless target.in_scope?(potential_url)
49
+
50
+ res = NS::Browser.post(potential_url, body: Digest::MD5.hexdigest(rand(999_999).to_s[0..5]))
51
+
52
+ next unless /<methodResponse>/i.match?(res&.body)
53
+
54
+ return NS::Model::XMLRPC.new(potential_url, confidence: 100, found_by: DIRECT_ACCESS)
55
+ end
56
+ nil
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'interesting_findings/headers'
4
+ require_relative 'interesting_findings/robots_txt'
5
+ require_relative 'interesting_findings/fantastico_fileslist'
6
+ require_relative 'interesting_findings/search_replace_db_2'
7
+ require_relative 'interesting_findings/xml_rpc'
8
+
9
+ module CMSScanner
10
+ module Finders
11
+ module InterestingFindings
12
+ # Interesting Files Finder
13
+ class Base
14
+ include IndependentFinder
15
+
16
+ # @param [ CMSScanner::Target ] target
17
+ def initialize(target)
18
+ %w[Headers RobotsTxt FantasticoFileslist SearchReplaceDB2 XMLRPC].each do |f|
19
+ finders << NS::Finders::InterestingFindings.const_get(f).new(target)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Formatter
5
+ # CLI Formatter
6
+ class Cli < Base
7
+ # @return [ String ]
8
+ def info_icon
9
+ green('[+]')
10
+ end
11
+
12
+ # @return [ String ]
13
+ def notice_icon
14
+ blue('[i]')
15
+ end
16
+
17
+ # @return [ String ]
18
+ def warning_icon
19
+ amber('[!]')
20
+ end
21
+
22
+ # @return [ String ]
23
+ def critical_icon
24
+ red('[!]')
25
+ end
26
+
27
+ # @param [ String ] text
28
+ # @return [ String ]
29
+ def bold(text)
30
+ colorize(text, 1)
31
+ end
32
+
33
+ # @param [ String ] text
34
+ # @return [ String ]
35
+ def red(text)
36
+ colorize(text, 31)
37
+ end
38
+
39
+ # @param [ String ] text
40
+ # @return [ String ]
41
+ def green(text)
42
+ colorize(text, 32)
43
+ end
44
+
45
+ # @param [ String ] text
46
+ # @return [ String ]
47
+ def amber(text)
48
+ colorize(text, 33)
49
+ end
50
+
51
+ # @param [ String ] text
52
+ # @return [ String ]
53
+ def blue(text)
54
+ colorize(text, 34)
55
+ end
56
+
57
+ # @param [ String ] text
58
+ # @param [ Integer ] color_code
59
+ # @return [ String ]
60
+ def colorize(text, color_code)
61
+ "\e[#{color_code}m#{text}\e[0m"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Formatter
5
+ # Because Reason https://github.com/wpscanteam/CMSScanner/issues/56
6
+ class CliNoColor < CliNoColour
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Formatter
5
+ # CLI No Colour Formatter
6
+ class CliNoColour < Cli
7
+ # Override to get the cli views
8
+ def format
9
+ 'cli'
10
+ end
11
+
12
+ def colorize(text, _color_code)
13
+ text
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Formatter
5
+ # JSON Formatter
6
+ class Json < Base
7
+ include Buffer
8
+
9
+ def beautify
10
+ puts JSON.pretty_generate(JSON.parse("{#{buffer.chomp.chomp(',')}}"))
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
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.
9
+ class FantasticoFileslist < InterestingFinding
10
+ # @return [ String ]
11
+ def to_s
12
+ @to_s ||= "Fantastico list found: #{url}"
13
+ end
14
+
15
+ # @return [ Array<String> ] The interesting files/dirs detected
16
+ def interesting_entries
17
+ results = []
18
+
19
+ entries.each do |entry|
20
+ next unless /(?:admin|\.log|\.sql|\.db)/i.match?(entry)
21
+
22
+ results << entry
23
+ end
24
+ results
25
+ end
26
+
27
+ def references
28
+ @references ||= {
29
+ url: ['https://web.archive.org/web/20140518040021/http://www.acunetix.com/vulnerabilities/fantastico-fileslist/']
30
+ }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMSScanner
4
+ module Model
5
+ # Interesting Headers
6
+ class Headers < InterestingFinding
7
+ # @return [ Hash ] The headers
8
+ def entries
9
+ res = NS::Browser.get(url)
10
+ return [] unless res&.headers
11
+
12
+ res.headers
13
+ end
14
+
15
+ # @return [ Array<String> ] The interesting headers detected
16
+ def interesting_entries
17
+ results = []
18
+
19
+ entries.each do |header, value|
20
+ next if known_headers.include?(header.downcase)
21
+
22
+ results << "#{header}: #{Array(value).join(', ')}"
23
+ end
24
+ results
25
+ end
26
+
27
+ # @return [ Array<String> ] Downcased known headers
28
+ def known_headers
29
+ %w[
30
+ age accept-ranges cache-control content-encoding content-length content-type connection date
31
+ etag expires keep-alive location last-modified link pragma set-cookie strict-transport-security
32
+ transfer-encoding vary x-cache x-content-security-policy x-content-type-options
33
+ x-frame-options x-language x-permitted-cross-domain-policies x-pingback x-varnish
34
+ x-webkit-csp x-xss-protection
35
+ ]
36
+ end
37
+
38
+ # @return [ String ]
39
+ def to_s
40
+ @to_s ||= 'Headers'
41
+ end
42
+ end
43
+ end
44
+ end