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.
- checksums.yaml +4 -4
- data/app/app.rb +23 -4
- data/app/controllers/core.rb +8 -6
- data/app/controllers/core/cli_options.rb +2 -0
- data/app/controllers/interesting_findings.rb +2 -0
- data/app/finders/interesting_findings.rb +2 -0
- data/app/finders/interesting_findings/fantastico_fileslist.rb +6 -8
- data/app/finders/interesting_findings/headers.rb +3 -1
- data/app/finders/interesting_findings/robots_txt.rb +5 -7
- data/app/finders/interesting_findings/search_replace_db_2.rb +8 -10
- data/app/finders/interesting_findings/xml_rpc.rb +8 -6
- data/app/formatters/cli.rb +2 -0
- data/app/formatters/cli_no_color.rb +2 -0
- data/app/formatters/cli_no_colour.rb +2 -0
- data/app/formatters/json.rb +2 -0
- data/app/models/fantastico_fileslist.rb +16 -12
- data/app/models/headers.rb +29 -25
- data/app/models/interesting_finding.rb +44 -40
- data/app/models/robots_txt.rb +18 -14
- data/app/models/user.rb +25 -21
- data/app/models/version.rb +45 -41
- data/app/models/xml_rpc.rb +58 -54
- data/lib/cms_scanner.rb +5 -85
- data/lib/cms_scanner/browser.rb +2 -0
- data/lib/cms_scanner/browser/actions.rb +13 -13
- data/lib/cms_scanner/browser/options.rb +2 -0
- data/lib/cms_scanner/cache/file_store.rb +2 -0
- data/lib/cms_scanner/cache/typhoeus.rb +2 -0
- data/lib/cms_scanner/controller.rb +2 -0
- data/lib/cms_scanner/controllers.rb +3 -1
- data/lib/cms_scanner/errors.rb +11 -0
- data/lib/cms_scanner/errors/http.rb +52 -51
- data/lib/cms_scanner/errors/scan.rb +10 -6
- data/lib/cms_scanner/exit_code.rb +2 -0
- data/lib/cms_scanner/finders.rb +2 -0
- data/lib/cms_scanner/finders/base_finders.rb +2 -0
- data/lib/cms_scanner/finders/finder.rb +3 -1
- data/lib/cms_scanner/finders/finder/breadth_first_dictionary_attack.rb +3 -1
- data/lib/cms_scanner/finders/finder/enumerator.rb +44 -15
- data/lib/cms_scanner/finders/finder/fingerprinter.rb +9 -21
- data/lib/cms_scanner/finders/finder/smart_url_checker.rb +2 -0
- data/lib/cms_scanner/finders/finder/smart_url_checker/findings.rb +2 -0
- data/lib/cms_scanner/finders/finding.rb +2 -0
- data/lib/cms_scanner/finders/findings.rb +2 -0
- data/lib/cms_scanner/finders/independent_finder.rb +2 -0
- data/lib/cms_scanner/finders/independent_finders.rb +2 -0
- data/lib/cms_scanner/finders/same_type_finder.rb +2 -0
- data/lib/cms_scanner/finders/same_type_finders.rb +2 -0
- data/lib/cms_scanner/finders/unique_finder.rb +2 -0
- data/lib/cms_scanner/finders/unique_finders.rb +2 -0
- data/lib/cms_scanner/formatter.rb +2 -0
- data/lib/cms_scanner/formatter/buffer.rb +3 -1
- data/lib/cms_scanner/helper.rb +2 -0
- data/lib/cms_scanner/numeric.rb +2 -0
- data/lib/cms_scanner/progressbar_null_output.rb +2 -0
- data/lib/cms_scanner/public_suffix/domain.rb +2 -0
- data/lib/cms_scanner/references.rb +2 -0
- data/lib/cms_scanner/scan.rb +86 -0
- data/lib/cms_scanner/target.rb +2 -0
- data/lib/cms_scanner/target/hashes.rb +2 -0
- data/lib/cms_scanner/target/platform.rb +2 -0
- data/lib/cms_scanner/target/platform/php.rb +4 -2
- data/lib/cms_scanner/target/scope.rb +2 -0
- data/lib/cms_scanner/target/server.rb +2 -0
- data/lib/cms_scanner/target/server/apache.rb +2 -0
- data/lib/cms_scanner/target/server/generic.rb +2 -0
- data/lib/cms_scanner/target/server/iis.rb +2 -0
- data/lib/cms_scanner/target/server/nginx.rb +2 -0
- data/lib/cms_scanner/typhoeus/hydra.rb +2 -0
- data/lib/cms_scanner/typhoeus/response.rb +2 -0
- data/lib/cms_scanner/version.rb +3 -1
- data/lib/cms_scanner/vulnerability.rb +2 -0
- data/lib/cms_scanner/web_site.rb +34 -2
- metadata +4 -6
- data/app/controllers.rb +0 -2
- data/app/finders.rb +0 -1
- data/app/formatters.rb +0 -4
- data/app/models.rb +0 -7
@@ -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::
|
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
|
@@ -1,70 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CMSScanner
|
2
|
-
|
3
|
-
|
4
|
+
module Error
|
5
|
+
# Target Down Error
|
6
|
+
class TargetDown < Standard
|
7
|
+
attr_reader :response
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
end
|
8
12
|
|
9
|
-
|
10
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
36
|
+
# Access Forbidden Error
|
37
|
+
class AccessForbidden < Standard
|
38
|
+
attr_reader :random_user_agent_used
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
56
|
+
# HTTP Redirect Error
|
57
|
+
class HTTPRedirect < Standard
|
58
|
+
attr_reader :redirect_uri
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
# @param [ String ] url
|
61
|
+
def initialize(url)
|
62
|
+
@redirect_uri = Addressable::URI.parse(url).normalize
|
63
|
+
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
data/lib/cms_scanner/finders.rb
CHANGED
@@ -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)'
|
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(
|
13
|
-
create_progress_bar(opts.merge(total:
|
28
|
+
def enumerate(urls, opts = {})
|
29
|
+
create_progress_bar(opts.merge(total: urls.size))
|
14
30
|
|
15
|
-
|
16
|
-
request = browser.forge_request(url,
|
31
|
+
urls.each do |url, id|
|
32
|
+
request = browser.forge_request(url, head_or_get_request_params)
|
17
33
|
|
18
|
-
request.on_complete do |
|
34
|
+
request.on_complete do |head_res|
|
19
35
|
progress_bar.increment
|
20
36
|
|
21
|
-
next
|
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
|
-
|
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
|
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
|
38
|
-
|
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
|
-
|
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
|
-
|
32
|
+
enumerate(fingerprints.transform_keys { |k| target.url(k) }, enum_opts) do |res, fingerprint|
|
33
|
+
md5sum = hexdigest(res.body)
|
36
34
|
|
37
|
-
|
35
|
+
next unless fingerprint.key?(md5sum)
|
38
36
|
|
39
|
-
|
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
|