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.
- 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
|