arachni 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 )
|