arachni 1.1 → 1.2
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/CHANGELOG.md +159 -0
- data/LICENSE.md +126 -196
- data/README.md +32 -24
- data/arachni.gemspec +7 -7
- data/components/checks/active/code_injection_timing.rb +3 -3
- data/components/checks/active/csrf.rb +2 -2
- data/components/checks/active/file_inclusion.rb +6 -7
- data/components/checks/active/os_cmd_injection.rb +3 -3
- data/components/checks/active/path_traversal.rb +7 -7
- data/components/checks/active/response_splitting.rb +9 -4
- data/components/checks/active/session_fixation.rb +7 -3
- data/components/checks/active/source_code_disclosure.rb +5 -5
- data/components/checks/active/unvalidated_redirect.rb +12 -3
- data/components/checks/active/unvalidated_redirect_dom.rb +3 -3
- data/components/checks/active/xss.rb +23 -10
- data/components/checks/active/xss_dom_inputs.rb +113 -11
- data/components/checks/active/xxe.rb +3 -3
- data/components/checks/passive/backdoors.rb +6 -5
- data/components/checks/passive/backup_directories.rb +6 -6
- data/components/checks/passive/backup_files.rb +6 -6
- data/components/checks/passive/common_admin_interfaces.rb +58 -0
- data/components/checks/passive/common_admin_interfaces/admin-panels.txt +49 -0
- data/components/checks/passive/common_directories/directories.txt +0 -16
- data/components/checks/passive/common_files.rb +6 -5
- data/components/checks/passive/common_files/filenames.txt +0 -2
- data/components/checks/passive/directory_listing.rb +6 -6
- data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +3 -3
- data/components/checks/passive/grep/hsts.rb +6 -3
- data/components/checks/passive/grep/http_only_cookies.rb +3 -3
- data/components/checks/passive/grep/insecure_cookies.rb +2 -2
- data/components/checks/passive/grep/insecure_cors_policy.rb +6 -4
- data/components/checks/passive/grep/x_frame_options.rb +6 -4
- data/components/checks/passive/htaccess_limit.rb +6 -2
- data/components/checks/passive/http_put.rb +8 -4
- data/components/checks/passive/interesting_responses.rb +3 -2
- data/components/checks/passive/localstart_asp.rb +6 -2
- data/components/checks/passive/origin_spoof_access_restriction_bypass.rb +5 -1
- data/components/checks/passive/xst.rb +6 -2
- data/components/fingerprinters/frameworks/aspx_mvc.rb +43 -0
- data/components/fingerprinters/frameworks/cakephp.rb +28 -0
- data/components/fingerprinters/frameworks/cherrypy.rb +31 -0
- data/components/fingerprinters/frameworks/django.rb +33 -0
- data/components/fingerprinters/frameworks/jsf.rb +30 -0
- data/components/fingerprinters/frameworks/rack.rb +5 -7
- data/components/fingerprinters/frameworks/rails.rb +43 -0
- data/components/fingerprinters/languages/aspx.rb +11 -11
- data/components/fingerprinters/languages/{jsp.rb → java.rb} +11 -7
- data/components/fingerprinters/languages/php.rb +6 -6
- data/components/fingerprinters/languages/python.rb +14 -6
- data/components/fingerprinters/languages/ruby.rb +3 -5
- data/components/fingerprinters/servers/apache.rb +5 -4
- data/components/fingerprinters/servers/gunicorn.rb +33 -0
- data/components/fingerprinters/servers/jetty.rb +1 -1
- data/components/fingerprinters/servers/tomcat.rb +11 -4
- data/components/path_extractors/anchors.rb +5 -12
- data/components/path_extractors/areas.rb +5 -13
- data/components/path_extractors/comments.rb +5 -3
- data/components/path_extractors/data_url.rb +21 -0
- data/components/path_extractors/forms.rb +5 -13
- data/components/path_extractors/frames.rb +6 -13
- data/components/path_extractors/generic.rb +3 -12
- data/components/path_extractors/links.rb +5 -13
- data/components/path_extractors/meta_refresh.rb +5 -13
- data/components/path_extractors/scripts.rb +8 -14
- data/components/plugins/autologin.rb +17 -5
- data/components/plugins/defaults/meta/remedies/discovery.rb +11 -29
- data/components/plugins/login_script.rb +40 -10
- data/components/plugins/metrics.rb +235 -0
- data/components/plugins/proxy.rb +21 -4
- data/components/plugins/proxy/panel/page_accordion.html.erb +34 -2
- data/components/plugins/restrict_to_dom_state.rb +70 -0
- data/components/plugins/vector_feed.rb +38 -9
- data/components/reporters/plugin_formatters/html/metrics.rb +290 -0
- data/components/reporters/plugin_formatters/stdout/metrics.rb +80 -0
- data/components/reporters/plugin_formatters/xml/metrics.rb +29 -0
- data/components/reporters/stdout.rb +4 -2
- data/components/reporters/xml.rb +4 -4
- data/components/reporters/xml/schema.xsd +95 -0
- data/lib/arachni.rb +2 -0
- data/lib/arachni/browser.rb +132 -77
- data/lib/arachni/browser/javascript.rb +173 -45
- data/lib/arachni/browser/javascript/scripts/dom_monitor.js +81 -6
- data/lib/arachni/browser/javascript/scripts/taint_tracer.js +31 -3
- data/lib/arachni/browser_cluster.rb +41 -15
- data/lib/arachni/browser_cluster/job.rb +4 -0
- data/lib/arachni/browser_cluster/jobs/resource_exploration.rb +0 -9
- data/lib/arachni/browser_cluster/worker.rb +8 -5
- data/lib/arachni/check/auditor.rb +20 -8
- data/lib/arachni/check/base.rb +38 -6
- data/lib/arachni/element/base.rb +18 -1
- data/lib/arachni/element/capabilities/analyzable/differential.rb +0 -1
- data/lib/arachni/element/capabilities/analyzable/taint.rb +40 -10
- data/lib/arachni/element/capabilities/analyzable/timeout.rb +27 -23
- data/lib/arachni/element/capabilities/auditable/dom.rb +22 -0
- data/lib/arachni/element/capabilities/inputtable.rb +6 -2
- data/lib/arachni/element/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/cookie.rb +37 -23
- data/lib/arachni/element/cookie/capabilities/mutable.rb +6 -6
- data/lib/arachni/element/cookie/dom.rb +0 -8
- data/lib/arachni/element/form.rb +28 -14
- data/lib/arachni/element/form/capabilities/auditable.rb +2 -2
- data/lib/arachni/element/form/capabilities/mutable.rb +5 -5
- data/lib/arachni/element/form/dom.rb +0 -8
- data/lib/arachni/element/generic_dom.rb +1 -1
- data/lib/arachni/element/json.rb +2 -1
- data/lib/arachni/element/json/capabilities/inputtable.rb +6 -6
- data/lib/arachni/element/json/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/link.rb +13 -16
- data/lib/arachni/element/link/dom.rb +1 -14
- data/lib/arachni/element/link_template.rb +3 -2
- data/lib/arachni/element/link_template/dom.rb +0 -16
- data/lib/arachni/element/server.rb +51 -9
- data/lib/arachni/element/xml.rb +1 -0
- data/lib/arachni/ethon/easy.rb +4 -1
- data/lib/arachni/framework/parts/audit.rb +26 -77
- data/lib/arachni/framework/parts/browser.rb +50 -55
- data/lib/arachni/framework/parts/check.rb +4 -3
- data/lib/arachni/framework/parts/data.rb +41 -6
- data/lib/arachni/framework/parts/state.rb +16 -7
- data/lib/arachni/http/client.rb +66 -38
- data/lib/arachni/http/client/dynamic_404_handler.rb +46 -14
- data/lib/arachni/http/headers.rb +22 -10
- data/lib/arachni/http/proxy_server.rb +67 -22
- data/lib/arachni/http/proxy_server/ssl-interceptor-cacert.pem +34 -0
- data/lib/arachni/http/proxy_server/ssl-interceptor-cakey.pem +51 -0
- data/lib/arachni/http/request.rb +71 -18
- data/lib/arachni/issue.rb +17 -3
- data/lib/arachni/option_groups/browser_cluster.rb +34 -1
- data/lib/arachni/option_groups/http.rb +1 -1
- data/lib/arachni/page.rb +26 -13
- data/lib/arachni/page/dom/transition.rb +2 -2
- data/lib/arachni/parser.rb +28 -11
- data/lib/arachni/platform/fingerprinter.rb +5 -0
- data/lib/arachni/platform/manager.rb +65 -32
- data/lib/arachni/plugin/base.rb +8 -0
- data/lib/arachni/processes/instances.rb +25 -11
- data/lib/arachni/reporter/manager.rb +2 -2
- data/lib/arachni/rpc/client/instance.rb +4 -0
- data/lib/arachni/rpc/server/framework/master.rb +3 -3
- data/lib/arachni/rpc/server/framework/multi_instance.rb +0 -8
- data/lib/arachni/rpc/server/instance.rb +2 -1
- data/lib/arachni/ruby/array.rb +5 -0
- data/lib/arachni/ruby/hash.rb +5 -0
- data/lib/arachni/ruby/string.rb +2 -3
- data/lib/arachni/session.rb +32 -6
- data/lib/arachni/state/framework.rb +6 -2
- data/lib/arachni/support/cache.rb +1 -0
- data/lib/arachni/support/cache/base.rb +12 -8
- data/lib/arachni/support/cache/least_recently_pushed.rb +29 -0
- data/lib/arachni/support/cache/least_recently_used.rb +5 -8
- data/lib/arachni/support/cache/preference.rb +1 -1
- data/lib/arachni/support/cache/random_replacement.rb +1 -25
- data/lib/arachni/support/database/queue.rb +21 -8
- data/lib/arachni/support/lookup/base.rb +7 -1
- data/lib/arachni/support/mixins/observable.rb +3 -1
- data/lib/arachni/support/profiler.rb +51 -10
- data/lib/arachni/support/signature.rb +11 -2
- data/lib/arachni/trainer.rb +8 -2
- data/lib/arachni/uri.rb +28 -25
- data/lib/arachni/uri/scope.rb +1 -1
- data/lib/arachni/utilities.rb +8 -0
- data/lib/arachni/watir/element.rb +1 -1
- data/lib/version +1 -1
- data/spec/arachni/browser/javascript/dom_monitor_spec.rb +388 -53
- data/spec/arachni/browser/javascript/taint_tracer_spec.rb +41 -0
- data/spec/arachni/browser/javascript_spec.rb +235 -61
- data/spec/arachni/browser_cluster/jobs/resource_exploration_spec.rb +0 -9
- data/spec/arachni/browser_cluster_spec.rb +58 -10
- data/spec/arachni/browser_spec.rb +170 -26
- data/spec/arachni/check/auditor_spec.rb +22 -3
- data/spec/arachni/check/base_spec.rb +84 -0
- data/spec/arachni/element/body_spec.rb +1 -1
- data/spec/arachni/element/capabilities/analyzable/taint_spec.rb +3 -3
- data/spec/arachni/element/capabilities/analyzable/timeout_spec.rb +1 -1
- data/spec/arachni/element/cookie/dom_spec.rb +0 -9
- data/spec/arachni/element/cookie_spec.rb +85 -0
- data/spec/arachni/element/form/dom_spec.rb +0 -9
- data/spec/arachni/element/form_spec.rb +46 -3
- data/spec/arachni/element/json_spec.rb +20 -0
- data/spec/arachni/element/link/dom_spec.rb +0 -9
- data/spec/arachni/element/link_spec.rb +40 -15
- data/spec/arachni/element/link_template/dom_spec.rb +0 -8
- data/spec/arachni/element/link_template_spec.rb +2 -6
- data/spec/arachni/element/server_spec.rb +94 -8
- data/spec/arachni/element/xml_spec.rb +20 -0
- data/spec/arachni/framework/parts/audit_spec.rb +12 -14
- data/spec/arachni/framework/parts/browser_spec.rb +0 -171
- data/spec/arachni/framework/parts/platform_spec.rb +14 -8
- data/spec/arachni/framework/parts/report_spec.rb +1 -1
- data/spec/arachni/framework/parts/state_spec.rb +0 -9
- data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +19 -0
- data/spec/arachni/http/client_spec.rb +169 -42
- data/spec/arachni/http/headers_spec.rb +18 -0
- data/spec/arachni/http/request_spec.rb +23 -0
- data/spec/arachni/issue_spec.rb +17 -6
- data/spec/arachni/page_spec.rb +22 -2
- data/spec/arachni/parser_spec.rb +5 -0
- data/spec/arachni/platform/manager_spec.rb +57 -25
- data/spec/arachni/reporter/manager_spec.rb +26 -0
- data/spec/arachni/rpc/server/active_options_spec.rb +9 -4
- data/spec/arachni/state/framework_spec.rb +2 -8
- data/spec/arachni/support/cache/least_recently_pushed_spec.rb +90 -0
- data/spec/arachni/support/cache/least_recently_used_spec.rb +5 -13
- data/spec/arachni/support/database/queue_spec.rb +7 -0
- data/spec/arachni/support/mixins/observable_spec.rb +15 -1
- data/spec/arachni/trainer_spec.rb +2 -2
- data/spec/components/checks/active/code_injection_timing_spec.rb +1 -1
- data/spec/components/checks/active/file_inclusion_spec.rb +6 -6
- data/spec/components/checks/active/path_traversal_spec.rb +2 -2
- data/spec/components/checks/active/source_code_disclosure_spec.rb +2 -2
- data/spec/components/checks/active/unvalidated_redirect_spec.rb +6 -6
- data/spec/components/checks/active/xss_dom_inputs_spec.rb +3 -5
- data/spec/components/checks/active/xss_dom_script_context_spec.rb +1 -1
- data/spec/components/checks/active/xss_spec.rb +5 -5
- data/spec/components/checks/passive/common_admin_interfaces_spec.rb +15 -0
- data/spec/components/checks/passive/interesting_responses_spec.rb +14 -1
- data/spec/components/fingerprinters/frameworks/aspx_mvc_spec.rb +31 -0
- data/spec/components/fingerprinters/frameworks/cakephp_spec.rb +22 -0
- data/spec/components/fingerprinters/frameworks/cherrypy_spec.rb +28 -0
- data/spec/components/fingerprinters/frameworks/django_spec.rb +37 -0
- data/spec/components/fingerprinters/frameworks/jsf_spec.rb +27 -0
- data/spec/components/fingerprinters/frameworks/rack_spec.rb +11 -14
- data/spec/components/fingerprinters/frameworks/rails_spec.rb +53 -0
- data/spec/components/fingerprinters/languages/asp_spec.rb +7 -9
- data/spec/components/fingerprinters/languages/aspx_spec.rb +10 -24
- data/spec/components/fingerprinters/languages/java_spec.rb +88 -0
- data/spec/components/fingerprinters/languages/php_spec.rb +19 -12
- data/spec/components/fingerprinters/languages/python_spec.rb +22 -9
- data/spec/components/fingerprinters/languages/ruby.rb +6 -4
- data/spec/components/fingerprinters/os/bsd_spec.rb +6 -4
- data/spec/components/fingerprinters/os/linux_spec.rb +6 -4
- data/spec/components/fingerprinters/os/solaris_spec.rb +6 -4
- data/spec/components/fingerprinters/os/unix_spec.rb +6 -4
- data/spec/components/fingerprinters/os/windows_spec.rb +6 -4
- data/spec/components/fingerprinters/servers/apache_spec.rb +15 -4
- data/spec/components/fingerprinters/servers/gunicorn_spec.rb +28 -0
- data/spec/components/fingerprinters/servers/iis_spec.rb +6 -6
- data/spec/components/fingerprinters/servers/jetty_spec.rb +6 -6
- data/spec/components/fingerprinters/servers/nginx_spec.rb +6 -4
- data/spec/components/fingerprinters/servers/tomcat_spec.rb +15 -6
- data/spec/components/path_extractors/data_url_spec.rb +19 -0
- data/spec/components/plugins/autologin_spec.rb +23 -0
- data/spec/components/plugins/login_script_spec.rb +112 -24
- data/spec/components/plugins/restrict_to_dom_state_spec.rb +16 -0
- data/spec/components/plugins/vector_feed_spec.rb +39 -1
- data/spec/support/factories/page/dom.rb +9 -4
- data/spec/support/factories/page/dom/transition.rb +31 -9
- data/spec/support/factories/scan_report.rb +8 -6
- data/spec/support/fixtures/empty/placeholder +0 -0
- data/spec/support/fixtures/report.afr +0 -0
- data/spec/support/fixtures/reporters/manager_spec/error.rb +18 -0
- data/spec/support/servers/arachni/browser.rb +117 -11
- data/spec/support/servers/arachni/browser/javascript/dom_monitor.rb +148 -4
- data/spec/support/servers/arachni/check/auditor.rb +4 -0
- data/spec/support/servers/arachni/element/cookie/cookie_dom.rb +1 -1
- data/spec/support/servers/arachni/http/client.rb +5 -0
- data/spec/support/servers/arachni/http/client/dynamic_404_handler.rb +13 -0
- data/spec/support/servers/checks/active/code_injection_timing.rb +1 -1
- data/spec/support/servers/checks/active/file_inclusion.rb +2 -2
- data/spec/support/servers/checks/active/path_traversal.rb +2 -2
- data/spec/support/servers/checks/active/source_code_disclosure.rb +40 -33
- data/spec/support/servers/checks/active/trainer_check.rb +9 -10
- data/spec/support/servers/checks/active/unvalidated_redirect_dom.rb +7 -4
- data/spec/support/servers/checks/active/xss.rb +35 -0
- data/spec/support/servers/checks/active/xss_dom.rb +1 -1
- data/spec/support/servers/checks/active/xss_dom_inputs.rb +24 -0
- data/spec/support/servers/checks/active/xss_dom_script_context.rb +1 -1
- data/spec/support/servers/checks/passive/common_admin_interfaces.rb +6 -0
- data/spec/support/servers/plugins/autologin.rb +9 -0
- data/spec/support/servers/plugins/restrict_to_dom_state.rb +4 -0
- data/spec/support/shared/element/base.rb +42 -0
- data/spec/support/shared/element/capabilities/auditable.rb +4 -4
- data/spec/support/shared/element/capabilities/auditable/dom.rb +26 -0
- data/spec/support/shared/element/capabilities/inputtable.rb +16 -11
- data/spec/support/shared/element/capabilities/submitable.rb +7 -2
- data/spec/support/shared/fingerprinter.rb +8 -0
- data/spec/support/shared/path_extractor.rb +1 -1
- data/ui/cli/framework.rb +3 -3
- data/ui/cli/framework/option_parser.rb +9 -0
- data/ui/cli/output.rb +9 -0
- data/ui/cli/reporter.rb +5 -2
- data/ui/cli/utilities.rb +4 -2
- metadata +76 -17
- data/lib/arachni/http/proxy_server/ssl-interceptor-cert.pem +0 -34
- data/lib/arachni/http/proxy_server/ssl-interceptor-pkey.pem +0 -51
- data/spec/components/fingerprinters/languages/jsp_spec.rb +0 -56
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
web site for more information on licensing and terms of use.
|
|
7
7
|
=end
|
|
8
8
|
|
|
9
|
-
require 'monitor'
|
|
10
|
-
|
|
11
9
|
module Arachni
|
|
12
10
|
|
|
13
11
|
# Real browser driver providing DOM/JS/AJAX support.
|
|
@@ -63,10 +61,6 @@ class BrowserCluster
|
|
|
63
61
|
# List of crawled URLs with their HTTP codes.
|
|
64
62
|
attr_reader :sitemap
|
|
65
63
|
|
|
66
|
-
# @return [String]
|
|
67
|
-
# Javascript token used to namespace the custom JS environment.
|
|
68
|
-
attr_reader :javascript_token
|
|
69
|
-
|
|
70
64
|
# @return [Array<Worker>]
|
|
71
65
|
# Worker pool.
|
|
72
66
|
attr_reader :workers
|
|
@@ -87,6 +81,8 @@ class BrowserCluster
|
|
|
87
81
|
#
|
|
88
82
|
# @raise ArgumentError On missing `:handler` option.
|
|
89
83
|
def initialize( options = {} )
|
|
84
|
+
super()
|
|
85
|
+
|
|
90
86
|
{
|
|
91
87
|
pool_size: Options.browser_cluster.pool_size
|
|
92
88
|
}.merge( options ).each do |k, v|
|
|
@@ -122,15 +118,16 @@ class BrowserCluster
|
|
|
122
118
|
@mutex = Monitor.new
|
|
123
119
|
@done_signal = Queue.new
|
|
124
120
|
|
|
125
|
-
# Javascript token to share across all workers, this needs to be static
|
|
126
|
-
# because of the browsers' disk-cache which leads to cached responses
|
|
127
|
-
# being used between runs.
|
|
128
|
-
@javascript_token = 'arachni_js_namespace'
|
|
129
|
-
|
|
130
121
|
@consumed_pids = []
|
|
131
122
|
initialize_workers
|
|
132
123
|
end
|
|
133
124
|
|
|
125
|
+
# @return [String]
|
|
126
|
+
# Javascript token used to namespace the custom JS environment.
|
|
127
|
+
def javascript_token
|
|
128
|
+
Browser::Javascript::TOKEN
|
|
129
|
+
end
|
|
130
|
+
|
|
134
131
|
# @note Operates in non-blocking mode.
|
|
135
132
|
#
|
|
136
133
|
# @param [Block] block
|
|
@@ -154,6 +151,8 @@ class BrowserCluster
|
|
|
154
151
|
synchronize do
|
|
155
152
|
print_debug "Queueing: #{job}"
|
|
156
153
|
|
|
154
|
+
notify_on_queue job
|
|
155
|
+
|
|
157
156
|
@pending_job_counter += 1
|
|
158
157
|
@pending_jobs[job.id] += 1
|
|
159
158
|
@job_callbacks[job.id] = block if block
|
|
@@ -168,6 +167,14 @@ class BrowserCluster
|
|
|
168
167
|
nil
|
|
169
168
|
end
|
|
170
169
|
|
|
170
|
+
# def on_queue( &block )
|
|
171
|
+
# synchronize { @on_queue << block }
|
|
172
|
+
# end
|
|
173
|
+
|
|
174
|
+
# def on_job_done( &block )
|
|
175
|
+
# synchronize { @on_job_done << block }
|
|
176
|
+
# end
|
|
177
|
+
|
|
171
178
|
# @param [Page, String, HTTP::Response] resource
|
|
172
179
|
# Resource to explore, if given a `String` it will be treated it as a URL
|
|
173
180
|
# and will be loaded.
|
|
@@ -206,6 +213,8 @@ class BrowserCluster
|
|
|
206
213
|
synchronize do
|
|
207
214
|
print_debug "Job done: #{job}"
|
|
208
215
|
|
|
216
|
+
notify_on_job_done job
|
|
217
|
+
|
|
209
218
|
if !job.never_ending?
|
|
210
219
|
@skip_states_per_job.delete job.id
|
|
211
220
|
@job_callbacks.delete job.id
|
|
@@ -299,6 +308,7 @@ class BrowserCluster
|
|
|
299
308
|
# @private
|
|
300
309
|
def pop
|
|
301
310
|
{} while job_done?( job = @jobs.pop )
|
|
311
|
+
notify_on_pop job
|
|
302
312
|
job
|
|
303
313
|
end
|
|
304
314
|
|
|
@@ -365,6 +375,23 @@ class BrowserCluster
|
|
|
365
375
|
|
|
366
376
|
private
|
|
367
377
|
|
|
378
|
+
def notify_on_queue( job )
|
|
379
|
+
return if !@on_queue
|
|
380
|
+
@on_queue.call job
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def notify_on_job_done( job )
|
|
384
|
+
return if !@on_job_done
|
|
385
|
+
|
|
386
|
+
@on_job_done.call job
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def notify_on_pop( job )
|
|
390
|
+
return if !@on_pop
|
|
391
|
+
|
|
392
|
+
@on_pop.call job
|
|
393
|
+
end
|
|
394
|
+
|
|
368
395
|
def fail_if_shutdown
|
|
369
396
|
fail Error::AlreadyShutdown, 'Cluster has been shut down.' if @shutdown
|
|
370
397
|
end
|
|
@@ -389,10 +416,9 @@ class BrowserCluster
|
|
|
389
416
|
@workers = []
|
|
390
417
|
pool_size.times do |i|
|
|
391
418
|
worker = Worker.new(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
height: Options.browser_cluster.screen_height
|
|
419
|
+
master: self,
|
|
420
|
+
width: Options.browser_cluster.screen_width,
|
|
421
|
+
height: Options.browser_cluster.screen_height
|
|
396
422
|
)
|
|
397
423
|
@workers << worker
|
|
398
424
|
@consumed_pids << worker.pid
|
|
@@ -101,6 +101,10 @@ class Job
|
|
|
101
101
|
# @param [Hash] data
|
|
102
102
|
# Used to initialize the {Result}.
|
|
103
103
|
def save_result( data )
|
|
104
|
+
# Results coming in after the job has already finished won't have a
|
|
105
|
+
# browser.
|
|
106
|
+
return if !browser
|
|
107
|
+
|
|
104
108
|
browser.master.handle_job_result(
|
|
105
109
|
self.class::Result.new( data.merge( job: self.clean_copy ) )
|
|
106
110
|
)
|
|
@@ -36,15 +36,6 @@ class ResourceExploration < Job
|
|
|
36
36
|
browser.trigger_events
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def resource=( resource )
|
|
40
|
-
# Get a copy of the page with the caches cleared, this way when the
|
|
41
|
-
# modules (or anything else) lazy-load elements and populate the caches
|
|
42
|
-
# there won't be any lingering references to them from the more time
|
|
43
|
-
# consuming browser analysis.
|
|
44
|
-
resource = resource.dup if resource.is_a? Page
|
|
45
|
-
@resource = resource
|
|
46
|
-
end
|
|
47
|
-
|
|
48
39
|
def dup
|
|
49
40
|
super.tap { |j| j.resource = resource }
|
|
50
41
|
end
|
|
@@ -45,7 +45,6 @@ class Worker < Arachni::Browser
|
|
|
45
45
|
attr_reader :time_to_live
|
|
46
46
|
|
|
47
47
|
def initialize( options = {} )
|
|
48
|
-
javascript_token = options.delete( :javascript_token )
|
|
49
48
|
@master = options.delete( :master )
|
|
50
49
|
|
|
51
50
|
@max_time_to_live = options.delete( :max_time_to_live ) ||
|
|
@@ -59,8 +58,6 @@ class Worker < Arachni::Browser
|
|
|
59
58
|
# as soon as they're logged.
|
|
60
59
|
super options.merge( store_pages: false )
|
|
61
60
|
|
|
62
|
-
@javascript.token = javascript_token
|
|
63
|
-
|
|
64
61
|
@done_signal = Queue.new
|
|
65
62
|
|
|
66
63
|
start_capture
|
|
@@ -90,13 +87,19 @@ class Worker < Arachni::Browser
|
|
|
90
87
|
exception_jail false do
|
|
91
88
|
begin
|
|
92
89
|
@job.configure_and_run( self )
|
|
93
|
-
rescue Selenium::WebDriver::Error::WebDriverError
|
|
90
|
+
rescue Selenium::WebDriver::Error::WebDriverError,
|
|
91
|
+
Watir::Exception::Error => e
|
|
92
|
+
|
|
93
|
+
print_debug "Error while processing job: #{@job}"
|
|
94
|
+
print_debug
|
|
95
|
+
print_debug_exception e
|
|
96
|
+
|
|
94
97
|
browser_respawn
|
|
95
98
|
end
|
|
96
99
|
end
|
|
97
100
|
end
|
|
98
101
|
rescue TimeoutError => e
|
|
99
|
-
|
|
102
|
+
print_bad "Job timed-out after #{@job_timeout} seconds: #{@job}"
|
|
100
103
|
|
|
101
104
|
# Could have left us with a broken browser.
|
|
102
105
|
browser_respawn
|
|
@@ -80,16 +80,21 @@ module Auditor
|
|
|
80
80
|
# Element types to check for.
|
|
81
81
|
#
|
|
82
82
|
# @return [Bool]
|
|
83
|
-
def self.check?( page, restrict_to_elements = nil )
|
|
83
|
+
def self.check?( page, restrict_to_elements = nil, ignore_dom_depth = false )
|
|
84
84
|
return false if issue_limit_reached?
|
|
85
85
|
return true if elements.empty?
|
|
86
86
|
|
|
87
87
|
audit = Arachni::Options.audit
|
|
88
88
|
restrict_to_elements = [restrict_to_elements].flatten.compact
|
|
89
89
|
|
|
90
|
+
# We use procs to make the decisions to avoid loading the page
|
|
91
|
+
# element caches unless it's absolutely necessary.
|
|
92
|
+
#
|
|
93
|
+
# Also, it's better to audit Form & Cookie DOM elements only
|
|
94
|
+
# after the page has gone through the browser, because then
|
|
95
|
+
# we'll have some context in the metadata which can help us
|
|
96
|
+
# optimize DOM audits.
|
|
90
97
|
{
|
|
91
|
-
# We use procs to make the decision, to avoid loading the page
|
|
92
|
-
# element caches unless it's absolutely necessary.
|
|
93
98
|
Element::Link =>
|
|
94
99
|
proc { audit.links? && !!page.links.find { |e| e.inputs.any? } },
|
|
95
100
|
Element::Link::DOM =>
|
|
@@ -97,20 +102,22 @@ module Auditor
|
|
|
97
102
|
Element::Form =>
|
|
98
103
|
proc { audit.forms? && !!page.forms.find { |e| e.inputs.any? } },
|
|
99
104
|
Element::Form::DOM =>
|
|
100
|
-
proc {
|
|
105
|
+
proc { (ignore_dom_depth || page.dom.depth > 0) &&
|
|
106
|
+
audit.form_doms? && page.has_script? && !!page.forms.find(&:dom) },
|
|
101
107
|
Element::Cookie =>
|
|
102
108
|
proc { audit.cookies? && page.cookies.any? },
|
|
103
109
|
Element::Cookie::DOM =>
|
|
104
|
-
proc {
|
|
110
|
+
proc { (ignore_dom_depth || page.dom.depth > 0) &&
|
|
111
|
+
audit.cookie_doms? && page.has_script? && page.cookies.any? },
|
|
105
112
|
Element::Header =>
|
|
106
113
|
proc { audit.headers? && page.headers.any? },
|
|
107
114
|
Element::LinkTemplate =>
|
|
108
115
|
proc { audit.link_templates? && page.link_templates.find { |e| e.inputs.any? } },
|
|
109
116
|
Element::LinkTemplate::DOM =>
|
|
110
117
|
proc { audit.link_template_doms? && !!page.link_templates.find(&:dom) },
|
|
111
|
-
Element::JSON
|
|
118
|
+
Element::JSON =>
|
|
112
119
|
proc { audit.jsons? && page.jsons.find { |e| e.inputs.any? } },
|
|
113
|
-
Element::XML
|
|
120
|
+
Element::XML =>
|
|
114
121
|
proc { audit.xmls? && page.xmls.find { |e| e.inputs.any? } },
|
|
115
122
|
Element::Body => !page.body.empty?,
|
|
116
123
|
Element::GenericDOM => page.has_script?,
|
|
@@ -348,17 +355,22 @@ module Auditor
|
|
|
348
355
|
# If `false`, a message will be printed to stdout containing the status of
|
|
349
356
|
# the operation.
|
|
350
357
|
#
|
|
358
|
+
# @return [Issue]
|
|
359
|
+
#
|
|
351
360
|
# @see #log_issue
|
|
352
361
|
def log_remote_file( page_or_response, silent = false )
|
|
353
362
|
page = page_or_response.is_a?( Page ) ?
|
|
354
363
|
page_or_response : page_or_response.to_page
|
|
355
364
|
|
|
356
|
-
log_issue(
|
|
365
|
+
issue = log_issue(
|
|
357
366
|
vector: Element::Server.new( page.url ),
|
|
367
|
+
proof: page.response.status_line,
|
|
358
368
|
page: page
|
|
359
369
|
)
|
|
360
370
|
|
|
361
371
|
print_ok( "Found #{page.url}" ) if !silent
|
|
372
|
+
|
|
373
|
+
issue
|
|
362
374
|
end
|
|
363
375
|
alias :log_remote_directory :log_remote_file
|
|
364
376
|
|
data/lib/arachni/check/base.rb
CHANGED
|
@@ -152,18 +152,50 @@ class Base < Component::Base
|
|
|
152
152
|
@platforms ||= [info[:platforms]].flatten.compact
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
-
# @
|
|
155
|
+
# @return [Bool]
|
|
156
|
+
# `true` if the check has specified platforms for which it does not apply.
|
|
157
|
+
#
|
|
158
|
+
# @see .platforms
|
|
159
|
+
def has_exempt_platforms?
|
|
160
|
+
exempt_platforms.any?
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @return [Array<Symbol>]
|
|
164
|
+
# Platforms not applicable to this check.
|
|
165
|
+
#
|
|
166
|
+
# @see .info
|
|
167
|
+
def exempt_platforms
|
|
168
|
+
@exempt_platforms ||= [info[:exempt_platforms]].flatten.compact
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @param [Array<Symbol, String>] resource_platforms
|
|
156
172
|
# List of platforms to check for support.
|
|
157
173
|
#
|
|
158
174
|
# @return [Boolean]
|
|
159
175
|
# `true` if any of the given platforms are supported, `false` otherwise.
|
|
160
|
-
def supports_platforms?(
|
|
161
|
-
|
|
176
|
+
def supports_platforms?( resource_platforms )
|
|
177
|
+
if resource_platforms.any? && has_exempt_platforms?
|
|
178
|
+
manager = Platform::Manager.new( exempt_platforms )
|
|
179
|
+
|
|
180
|
+
resource_platforms.each do |p|
|
|
181
|
+
|
|
182
|
+
# When we check for exempt platforms we're looking for info
|
|
183
|
+
# from the same type.
|
|
184
|
+
ptype = Platform::Manager.find_type( p )
|
|
185
|
+
type_manager = manager.send( ptype )
|
|
186
|
+
|
|
187
|
+
return false if type_manager.pick( p => true ).any?
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
return true if resource_platforms.empty? || !has_platforms?
|
|
162
192
|
|
|
163
193
|
# Determine if we've got anything for the given platforms, the same
|
|
164
194
|
# way payloads are picked.
|
|
165
|
-
foo_data = self.platforms.
|
|
166
|
-
|
|
195
|
+
foo_data = self.platforms.
|
|
196
|
+
inject({}) { |h, platform| h.merge!( platform => true ) }
|
|
197
|
+
|
|
198
|
+
Platform::Manager.new( resource_platforms ).pick( foo_data ).any?
|
|
167
199
|
end
|
|
168
200
|
|
|
169
201
|
# @return [Array<Symbol>]
|
|
@@ -193,7 +225,7 @@ class Base < Component::Base
|
|
|
193
225
|
|
|
194
226
|
# @private
|
|
195
227
|
def clear_info_cache
|
|
196
|
-
@elements = @platforms = nil
|
|
228
|
+
@elements = @exempt_platforms = @platforms = nil
|
|
197
229
|
end
|
|
198
230
|
end
|
|
199
231
|
|
data/lib/arachni/element/base.rb
CHANGED
|
@@ -38,6 +38,19 @@ class Base
|
|
|
38
38
|
|
|
39
39
|
include Capabilities::WithScope
|
|
40
40
|
|
|
41
|
+
# Maximum element size in bytes.
|
|
42
|
+
# Anything larger than this should be exempt from parse and storage or have
|
|
43
|
+
# its value ignored.
|
|
44
|
+
#
|
|
45
|
+
# During the audit, thousands of copies will be generated and the same
|
|
46
|
+
# amount of HTP requests will be stored in the HTTP::Client queue.
|
|
47
|
+
# Thus, elements with inputs of excessive size will lead to excessive RAM
|
|
48
|
+
# consumption.
|
|
49
|
+
#
|
|
50
|
+
# This will almost never be necessary, but there have been cases of
|
|
51
|
+
# buggy `_VIEWSTATE` inputs that grow infinitely.
|
|
52
|
+
MAX_SIZE = 10_000
|
|
53
|
+
|
|
41
54
|
# @return [Page]
|
|
42
55
|
# Page this element belongs to.
|
|
43
56
|
attr_accessor :page
|
|
@@ -124,7 +137,7 @@ class Base
|
|
|
124
137
|
# @return [Symbol]
|
|
125
138
|
# Element type.
|
|
126
139
|
def self.type
|
|
127
|
-
name.split( ':' ).last.downcase.to_sym
|
|
140
|
+
@type ||= name.split( ':' ).last.downcase.to_sym
|
|
128
141
|
end
|
|
129
142
|
|
|
130
143
|
def dup
|
|
@@ -189,6 +202,10 @@ class Base
|
|
|
189
202
|
instance
|
|
190
203
|
end
|
|
191
204
|
|
|
205
|
+
def self.too_big?( element )
|
|
206
|
+
(element.is_a?( Numeric ) ? element : element.to_s.size) >= MAX_SIZE
|
|
207
|
+
end
|
|
208
|
+
|
|
192
209
|
end
|
|
193
210
|
end
|
|
194
211
|
end
|
|
@@ -15,6 +15,10 @@ module Analyzable
|
|
|
15
15
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
|
16
16
|
module Taint
|
|
17
17
|
|
|
18
|
+
TAINT_CACHE = {
|
|
19
|
+
match: Support::Cache::LeastRecentlyPushed.new( 10_000 )
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
TAINT_OPTIONS = {
|
|
19
23
|
# The regular expression to match against the response body.
|
|
20
24
|
#
|
|
@@ -99,28 +103,38 @@ module Taint
|
|
|
99
103
|
end
|
|
100
104
|
|
|
101
105
|
def match_patterns( patterns, matcher, response, opts )
|
|
106
|
+
k = [patterns, response.body]
|
|
107
|
+
return if TAINT_CACHE[:match][k] == false
|
|
108
|
+
|
|
102
109
|
if opts[:longest_word_optimization]
|
|
103
110
|
opts[:downcased_body] = response.body.downcase
|
|
104
111
|
end
|
|
105
112
|
|
|
106
113
|
case patterns
|
|
107
114
|
when Regexp, String, Array
|
|
108
|
-
[patterns].flatten.compact.
|
|
109
|
-
|
|
115
|
+
[patterns].flatten.compact.each do |pattern|
|
|
116
|
+
res = matcher.call( pattern, response, opts )
|
|
117
|
+
TAINT_CACHE[:match][k] ||= !!res
|
|
118
|
+
end
|
|
110
119
|
|
|
111
120
|
when Hash
|
|
112
121
|
if opts[:platform] && patterns[opts[:platform]]
|
|
113
122
|
[patterns[opts[:platform]]].flatten.compact.each do |p|
|
|
114
|
-
[p].flatten.compact.
|
|
115
|
-
|
|
123
|
+
[p].flatten.compact.each do |pattern|
|
|
124
|
+
res = matcher.call( pattern, response, opts )
|
|
125
|
+
TAINT_CACHE[:match][k] ||= !!res
|
|
126
|
+
end
|
|
116
127
|
end
|
|
128
|
+
|
|
117
129
|
else
|
|
118
130
|
patterns.each do |platform, p|
|
|
119
131
|
dopts = opts.dup
|
|
120
132
|
dopts[:platform] = platform
|
|
121
133
|
|
|
122
|
-
[p].flatten.compact.
|
|
123
|
-
|
|
134
|
+
[p].flatten.compact.each do |pattern|
|
|
135
|
+
res = matcher.call( pattern, response, dopts )
|
|
136
|
+
TAINT_CACHE[:match][k] ||= !!res
|
|
137
|
+
end
|
|
124
138
|
end
|
|
125
139
|
end
|
|
126
140
|
|
|
@@ -133,15 +147,22 @@ module Taint
|
|
|
133
147
|
dopts = opts.dup
|
|
134
148
|
dopts[:platform] = platform
|
|
135
149
|
|
|
136
|
-
[p].flatten.compact.
|
|
137
|
-
|
|
150
|
+
[p].flatten.compact.each do |pattern|
|
|
151
|
+
res = matcher.call( pattern, response, dopts )
|
|
152
|
+
TAINT_CACHE[:match][k] ||= !!res
|
|
153
|
+
end
|
|
138
154
|
end
|
|
139
155
|
end
|
|
140
156
|
end
|
|
141
157
|
|
|
142
158
|
def match_substring_and_log( substring, response, opts )
|
|
143
159
|
return if substring.to_s.empty?
|
|
144
|
-
|
|
160
|
+
|
|
161
|
+
k = [substring, response.body]
|
|
162
|
+
return if TAINT_CACHE[:match][k] == false
|
|
163
|
+
|
|
164
|
+
TAINT_CACHE[:match][k] = includes = response.body.include?( substring )
|
|
165
|
+
return if !includes || ignore?( response, opts )
|
|
145
166
|
|
|
146
167
|
@candidate_issues << {
|
|
147
168
|
response: response,
|
|
@@ -151,9 +172,14 @@ module Taint
|
|
|
151
172
|
vector: response.request.performer
|
|
152
173
|
}
|
|
153
174
|
setup_verification_callbacks
|
|
175
|
+
|
|
176
|
+
true
|
|
154
177
|
end
|
|
155
178
|
|
|
156
179
|
def match_regexp_and_log( regexp, response, opts )
|
|
180
|
+
k = [regexp, response.body]
|
|
181
|
+
return if TAINT_CACHE[:match][k] == false
|
|
182
|
+
|
|
157
183
|
regexp = regexp.is_a?( Regexp ) ? regexp :
|
|
158
184
|
Regexp.new( regexp.to_s, Regexp::IGNORECASE )
|
|
159
185
|
|
|
@@ -166,7 +192,9 @@ module Taint
|
|
|
166
192
|
|
|
167
193
|
match_data = match_data[0].to_s
|
|
168
194
|
|
|
169
|
-
|
|
195
|
+
TAINT_CACHE[:match][k] = !match_data.empty?
|
|
196
|
+
|
|
197
|
+
return if match_data.empty? || ignore?( response, opts )
|
|
170
198
|
|
|
171
199
|
@candidate_issues << {
|
|
172
200
|
response: response,
|
|
@@ -176,6 +204,8 @@ module Taint
|
|
|
176
204
|
vector: response.request.performer
|
|
177
205
|
}
|
|
178
206
|
setup_verification_callbacks
|
|
207
|
+
|
|
208
|
+
true
|
|
179
209
|
end
|
|
180
210
|
|
|
181
211
|
def ignore?( res, opts )
|