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
@@ -161,16 +161,14 @@ class Dynamic404Handler
|
|
161
161
|
current_signature = (preliminary_signatures_for( url )[i] ||= {})
|
162
162
|
|
163
163
|
PRECISION.times do
|
164
|
-
|
165
|
-
# This is important, helps us reduce waiting callers.
|
166
|
-
high_priority: true,
|
167
|
-
performer: self
|
168
|
-
) do |c_res|
|
164
|
+
request( generator.call ) do |c_res|
|
169
165
|
next if corrupted
|
170
166
|
|
167
|
+
print_debug "#{__method__} [gathering]: #{c_res.request.url} #{c_res.url} #{c_res.code} #{block}"
|
168
|
+
|
171
169
|
# Well, bad luck, bail out to avoid FPs.
|
172
|
-
if c_res
|
173
|
-
print_debug "#{__method__} [corrupted]: #{url} #{block}"
|
170
|
+
if corrupted_response?( c_res )
|
171
|
+
print_debug "#{__method__} [corrupted]: #{url} #{c_res.code} #{block}"
|
174
172
|
corrupted = true
|
175
173
|
next clear_data_for( url )
|
176
174
|
end
|
@@ -217,15 +215,13 @@ class Dynamic404Handler
|
|
217
215
|
current_signature = (advanced_signatures_for( url )[i] ||= {})
|
218
216
|
|
219
217
|
PRECISION.times do
|
220
|
-
|
221
|
-
# This is important, helps us reduce waiting callers.
|
222
|
-
high_priority: true,
|
223
|
-
performer: self
|
224
|
-
) do |c_res|
|
218
|
+
request( generator.call ) do |c_res|
|
225
219
|
next if corrupted
|
226
220
|
|
221
|
+
print_debug "#{__method__} [gathering]: #{c_res.request.url} #{c_res.url} #{c_res.code} #{block}"
|
222
|
+
|
227
223
|
# Well, bad luck, bail out to avoid FPs.
|
228
|
-
if c_res
|
224
|
+
if corrupted_response?( c_res )
|
229
225
|
print_debug "#{__method__} [corrupted]: #{url} #{block}"
|
230
226
|
corrupted = true
|
231
227
|
next clear_data_for( url )
|
@@ -305,7 +301,20 @@ class Dynamic404Handler
|
|
305
301
|
def needs_advanced_analysis?( url )
|
306
302
|
uri = uri_parse( url )
|
307
303
|
resource_name = uri.resource_name.to_s.split('.').tap(&:pop).join('.')
|
308
|
-
!!(
|
304
|
+
!!(
|
305
|
+
!resource_name.empty? ||
|
306
|
+
uri.resource_extension ||
|
307
|
+
uri.resource_name.to_s.include?( '~' )
|
308
|
+
)
|
309
|
+
end
|
310
|
+
|
311
|
+
# If this is neither a regular 404 nor a 202 the server probably freaked out
|
312
|
+
# -- 500 errors under stress and the like.
|
313
|
+
#
|
314
|
+
# In that case we should bail out to avoid corrupted signatures which can
|
315
|
+
# lead to FPs.
|
316
|
+
def corrupted_response?( response )
|
317
|
+
response.code != 404 && response.code != 200
|
309
318
|
end
|
310
319
|
|
311
320
|
def url_for( url )
|
@@ -387,9 +396,32 @@ class Dynamic404Handler
|
|
387
396
|
probes << proc { up_to_path + random_string + '.' + resource_extension }
|
388
397
|
end
|
389
398
|
|
399
|
+
if uri.resource_name.include?( '~' )
|
400
|
+
probes << proc {
|
401
|
+
up_to_path.sub(
|
402
|
+
uri.resource_name,
|
403
|
+
resource_name.gsub( '~', '~~' )
|
404
|
+
)
|
405
|
+
}
|
406
|
+
end
|
407
|
+
|
390
408
|
probes
|
391
409
|
end
|
392
410
|
|
411
|
+
def request( url, &block )
|
412
|
+
Client.get( url,
|
413
|
+
# This is important, helps us reduce waiting callers.
|
414
|
+
high_priority: true,
|
415
|
+
|
416
|
+
# We're going to be checking for a lot of non-existent resources,
|
417
|
+
# don't bother fingerprinting them
|
418
|
+
fingerprint: false,
|
419
|
+
|
420
|
+
performer: self,
|
421
|
+
&block
|
422
|
+
)
|
423
|
+
end
|
424
|
+
|
393
425
|
def data_for( url )
|
394
426
|
@signatures[url_for( url )] ||= signature_prototype
|
395
427
|
end
|
data/lib/arachni/http/headers.rb
CHANGED
@@ -18,13 +18,28 @@ module HTTP
|
|
18
18
|
# @author Tasos Laskos <tasos.laskos@arachni-scanner.com>
|
19
19
|
class Headers < Hash
|
20
20
|
|
21
|
+
FORMATTED_NAMES_CACHE = Support::Cache::LeastRecentlyPushed.new( 100 )
|
22
|
+
|
23
|
+
CONTENT_TYPE = 'content-type'
|
24
|
+
SET_COOKIE = 'set-cookie'
|
25
|
+
LOCATION = 'location'
|
26
|
+
|
21
27
|
# @param [Headers, Hash] headers
|
22
28
|
def initialize( headers = {} )
|
23
29
|
merge!( headers || {} )
|
24
30
|
end
|
25
31
|
|
26
32
|
def merge!( headers )
|
27
|
-
headers.each
|
33
|
+
headers.each do |k, v|
|
34
|
+
# Handle headers with identical normalized names, like a mixture of
|
35
|
+
# Set-Cookie and SET-COOKIE.
|
36
|
+
if include? k
|
37
|
+
self[k] = [self[k]].flatten
|
38
|
+
self[k] << v
|
39
|
+
else
|
40
|
+
self[k] = v
|
41
|
+
end
|
42
|
+
end
|
28
43
|
end
|
29
44
|
|
30
45
|
# @note `field` will be capitalized appropriately before storing.
|
@@ -77,20 +92,20 @@ class Headers < Hash
|
|
77
92
|
# @return [String, nil]
|
78
93
|
# Value of the `Content-Type` field.
|
79
94
|
def content_type
|
80
|
-
self[
|
95
|
+
self[CONTENT_TYPE]
|
81
96
|
end
|
82
97
|
|
83
98
|
# @return [String, nil]
|
84
99
|
# Value of the `Location` field.
|
85
100
|
def location
|
86
|
-
self[
|
101
|
+
self[LOCATION]
|
87
102
|
end
|
88
103
|
|
89
104
|
# @return [Array<String>]
|
90
105
|
# Set-cookie strings.
|
91
106
|
def set_cookie
|
92
|
-
return [] if self[
|
93
|
-
[self[
|
107
|
+
return [] if self[SET_COOKIE].to_s.empty?
|
108
|
+
[self[SET_COOKIE]].flatten
|
94
109
|
end
|
95
110
|
|
96
111
|
# @return [Array<Hash>]
|
@@ -119,15 +134,12 @@ class Headers < Hash
|
|
119
134
|
end
|
120
135
|
|
121
136
|
def self.format_field_name( field )
|
122
|
-
# return field
|
123
|
-
|
124
137
|
# If there's a '--' somewhere in there then skip it, it probably is an
|
125
138
|
# audit payload.
|
126
139
|
return field if field.include?( '--' )
|
127
140
|
|
128
|
-
|
129
|
-
|
130
|
-
field.to_s.split( '-' ).map( &:capitalize ).join( '-' )
|
141
|
+
FORMATTED_NAMES_CACHE[field] ||=
|
142
|
+
field.split( '-' ).map( &:capitalize ).join( '-' )
|
131
143
|
end
|
132
144
|
|
133
145
|
end
|
@@ -21,11 +21,17 @@ module HTTP
|
|
21
21
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
22
22
|
class ProxyServer < WEBrick::HTTPProxyServer
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
CACHE = {
|
25
|
+
format_field_name: Support::Cache::LeastRecentlyPushed.new( 100 )
|
26
|
+
}
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
SKIP_HEADERS = Set.new( HopByHop | ['content-encoding'] )
|
29
|
+
|
30
|
+
INTERCEPTOR_CA_CERTIFICATE =
|
31
|
+
File.dirname( __FILE__ ) + '/proxy_server/ssl-interceptor-cacert.pem'
|
32
|
+
|
33
|
+
INTERCEPTOR_CA_KEY =
|
34
|
+
File.dirname( __FILE__ ) + '/proxy_server/ssl-interceptor-cakey.pem'
|
29
35
|
|
30
36
|
# @param [Hash] options
|
31
37
|
# @option options [String] :address ('0.0.0.0')
|
@@ -168,8 +174,12 @@ class ProxyServer < WEBrick::HTTPProxyServer
|
|
168
174
|
# @see #service
|
169
175
|
# @see Webrick::HTTPProxyServer#service
|
170
176
|
def do_CONNECT( req, res )
|
177
|
+
host = req.unparsed_uri.split(':').first
|
178
|
+
|
171
179
|
req.instance_variable_set( :@unparsed_uri, "127.0.0.1:#{interceptor_port}" )
|
172
|
-
|
180
|
+
|
181
|
+
start_ssl_interceptor( host )
|
182
|
+
|
173
183
|
super( req, res )
|
174
184
|
end
|
175
185
|
|
@@ -177,8 +187,10 @@ class ProxyServer < WEBrick::HTTPProxyServer
|
|
177
187
|
# Merges the given HTTP options with some default ones.
|
178
188
|
def http_opts( options = {} )
|
179
189
|
options.merge(
|
190
|
+
performer: self,
|
191
|
+
|
180
192
|
# Don't follow redirects, the client should handle this.
|
181
|
-
follow_location:
|
193
|
+
follow_location: false,
|
182
194
|
|
183
195
|
# Set the HTTP request timeout.
|
184
196
|
timeout: @options[:timeout],
|
@@ -198,18 +210,54 @@ class ProxyServer < WEBrick::HTTPProxyServer
|
|
198
210
|
# Starts the SSL interceptor proxy server.
|
199
211
|
#
|
200
212
|
# The interceptor will listen on {#interceptor_port}.
|
201
|
-
def start_ssl_interceptor
|
213
|
+
def start_ssl_interceptor( host )
|
202
214
|
return @interceptor if @interceptor
|
203
215
|
|
216
|
+
ca = OpenSSL::X509::Certificate.new( File.read( INTERCEPTOR_CA_CERTIFICATE ) )
|
217
|
+
ca_key = OpenSSL::PKey::RSA.new( File.read( INTERCEPTOR_CA_KEY ) )
|
218
|
+
|
219
|
+
keypair = OpenSSL::PKey::RSA.new( 4096 )
|
220
|
+
|
221
|
+
req = OpenSSL::X509::Request.new
|
222
|
+
req.version = 0
|
223
|
+
req.subject = OpenSSL::X509::Name.parse(
|
224
|
+
"CN=#{host}/subjectAltName=#{host}/O=Arachni/OU=Proxy/L=Athens/ST=Attika/C=GR"
|
225
|
+
)
|
226
|
+
req.public_key = keypair.public_key
|
227
|
+
req.sign( keypair, OpenSSL::Digest::SHA1.new )
|
228
|
+
|
229
|
+
cert = OpenSSL::X509::Certificate.new
|
230
|
+
cert.version = 2
|
231
|
+
cert.serial = rand( 999999 )
|
232
|
+
cert.not_before = Time.new
|
233
|
+
cert.not_after = cert.not_before + (60 * 60 * 24 * 365)
|
234
|
+
cert.public_key = req.public_key
|
235
|
+
cert.subject = req.subject
|
236
|
+
cert.issuer = ca.subject
|
237
|
+
|
238
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
239
|
+
ef.subject_certificate = cert
|
240
|
+
ef.issuer_certificate = ca
|
241
|
+
|
242
|
+
cert.extensions = [
|
243
|
+
ef.create_extension( 'basicConstraints', 'CA:FALSE', true ),
|
244
|
+
ef.create_extension( 'extendedKeyUsage', 'serverAuth', false ),
|
245
|
+
ef.create_extension( 'subjectKeyIdentifier', 'hash' ),
|
246
|
+
ef.create_extension( 'authorityKeyIdentifier', 'keyid:always,issuer:always' ),
|
247
|
+
ef.create_extension( 'keyUsage',
|
248
|
+
'nonRepudiation,digitalSignature,keyEncipherment,dataEncipherment',
|
249
|
+
true
|
250
|
+
)
|
251
|
+
]
|
252
|
+
cert.sign( ca_key, OpenSSL::Digest::SHA1.new )
|
253
|
+
|
204
254
|
# The interceptor is only used for SSL decryption/encryption, the actual
|
205
255
|
# proxy functionality is forwarded to the plain proxy server.
|
206
256
|
@interceptor = self.class.new(
|
207
257
|
address: '127.0.0.1',
|
208
258
|
port: interceptor_port,
|
209
|
-
ssl_certificate:
|
210
|
-
|
211
|
-
ssl_private_key:
|
212
|
-
OpenSSL::PKey::RSA.new( File.read( INTERCEPTOR_PRIVATE_KEY ) ),
|
259
|
+
ssl_certificate: cert,
|
260
|
+
ssl_private_key: keypair,
|
213
261
|
service_handler: method( :proxy_service )
|
214
262
|
)
|
215
263
|
|
@@ -283,24 +331,21 @@ class ProxyServer < WEBrick::HTTPProxyServer
|
|
283
331
|
# @param [#[]=] dst
|
284
332
|
# Headers of the forwarded/proxy response.
|
285
333
|
def choose_header( src, dst )
|
286
|
-
connections = split_field( [src['connection']].flatten.first )
|
334
|
+
connections = Set.new( split_field( [src['connection']].flatten.first ) )
|
287
335
|
|
288
336
|
src.each do |key, value|
|
289
337
|
key = key.downcase
|
338
|
+
next if SKIP_HEADERS.include?( key ) || connections.include?( key )
|
290
339
|
|
291
|
-
|
292
|
-
connections.member?( key ) || # RFC2616: 14.10
|
293
|
-
key == 'content-encoding'
|
294
|
-
@logger.debug( "choose_header: `#{key}: #{value}'" )
|
295
|
-
next
|
296
|
-
end
|
297
|
-
|
298
|
-
field = key.to_s.split( /_|-/ ).
|
299
|
-
map { |segment| segment.capitalize }.join( '-' )
|
300
|
-
dst[field] = value
|
340
|
+
dst[self.class.format_field_name( key )] = value
|
301
341
|
end
|
302
342
|
end
|
303
343
|
|
344
|
+
def self.format_field_name( field )
|
345
|
+
CACHE[:format_field_name][field] ||=
|
346
|
+
field.split( /_|-/ ).map( &:capitalize ).join( '-' )
|
347
|
+
end
|
348
|
+
|
304
349
|
end
|
305
350
|
end
|
306
351
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIF6zCCA9OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMCR1Ix
|
3
|
+
DzANBgNVBAgMBkF0dGlrYTEPMA0GA1UEBwwGQXRoZW5zMRAwDgYDVQQKDAdBcmFj
|
4
|
+
aG5pMQ4wDAYDVQQLDAVQcm94eTEQMA4GA1UEAwwHQXJhY2huaTEqMCgGCSqGSIb3
|
5
|
+
DQEJARYbYXJhY2huaUBhcmFjaG5pLXNjYW5uZXIuY29tMB4XDTE1MDYxNDAyMDgz
|
6
|
+
MVoXDTI1MDYxMTAyMDgzMVowgY8xCzAJBgNVBAYTAkdSMQ8wDQYDVQQIDAZBdHRp
|
7
|
+
a2ExDzANBgNVBAcMBkF0aGVuczEQMA4GA1UECgwHQXJhY2huaTEOMAwGA1UECwwF
|
8
|
+
UHJveHkxEDAOBgNVBAMMB0FyYWNobmkxKjAoBgkqhkiG9w0BCQEWG2FyYWNobmlA
|
9
|
+
YXJhY2huaS1zY2FubmVyLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
|
10
|
+
ggIBAKfH1iQiuG8zYS514F8zZLp8//00gPIML7+wcJn4cw2iN+yEix6RuZEIzqva
|
11
|
+
TfC4H6lZTyUhsoGZCeYAzzH9BMri/9uHJF+4worPfVKYIsm3TnMOVYoIC1kP1/Gj
|
12
|
+
Y1ih8KI/3baw0pddtJJeQ5/GjDaxx4+ynY4ZxrNcFmbTYXSrPcd62V/D4+edVnLi
|
13
|
+
uQsUezWYx7gNFiAuPRtlgJVBwzRoPV+Fh7Es2/SfmNSfGBCCYOpj4Fh0GOv7pSV0
|
14
|
+
9TeF1W/XqDoq/eZ7RzLXoFK0Rz70/22MnFWAIdEUHZqwh3ktndNEK2QHq9FRGUx8
|
15
|
+
cUlXVAJgYv8tTErYVBltKIi2qgbnkh0Rb+rT2OkgmSL9lg0PwpXChMeSo6o6riC7
|
16
|
+
5a8PQi6OmIseY742QYmBXApXDHtSzaY8onHUvqgxFrFpP0Bmca3AoF6kWQfXfRwS
|
17
|
+
ClMLwfBBDVeb+Tt97MO1G4m2VEW6c7o9H1t4td55LGslUzfJrmFe99vjAtdRTVqG
|
18
|
+
t3qDjbYi5VpE9kIyKcPHZkSKelMQ4VO1qB14CdaK/3ufqHTk7Ro2hKgstKDqnTCF
|
19
|
+
R7Qb9yXFsb1QyNtW8898T5mQm0HWQxdkaxcodizVjY5inHgqNwPa7A469EYFm5in
|
20
|
+
dLSOQtdPOV4q+y5lfhA2MkE3pRdSZPpnTqCEkSVVoKfdVlftAgMBAAGjUDBOMB0G
|
21
|
+
A1UdDgQWBBRvmR7gGqIfTQB0GygwgI22Kyr1bDAfBgNVHSMEGDAWgBRvmR7gGqIf
|
22
|
+
TQB0GygwgI22Kyr1bDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQAx
|
23
|
+
g+ZjxJZXW1dYkc9ItXwAZba7oQJapLPu1iWCFy5cU13gck2MwDqfaDApNdr+erHg
|
24
|
+
WN7N+smMO+x3+lZZptzTfc6g/hBthBBAnetj8CUehjnWCo3aBGgVLE/mIEyHyFym
|
25
|
+
JX6xgcNYpvEzHT2o3Kmu/dAHCqY/3P9NtGJMhf7fy/Zz72tGY+ZTlthFSGWOjIEV
|
26
|
+
KXTtYnRUKmIRBLMacZmrJKIZCp/qGVSnFh9yjxHTWPNXXngGMxF9ItsFbdakjefn
|
27
|
+
hi2sHqns6/YbMaD2wK42dRQH1wH66DCGbyDPQO2j8iGK1q4Ggps+mGNYNBzMSAO/
|
28
|
+
ybdGRLQNq8ag7RXr/tNp/jYHopS/Ga0+3bOnCKf6MXNOolknSZhsOo16BWKDRd+d
|
29
|
+
m9ZTlro9AQr9+jdychG41IQNHXySrC5F1jLtzpEE5CJZIXkEFNYRcO9HMByJ3qwG
|
30
|
+
759oYcMklwhU+NSC5qXpD2Z9KGf5rc0HmoO6OyD4T8hnQXkuAqoIN/NBg6YSNisN
|
31
|
+
H2C2gbl+taRLt0/RVCiacylo5pl3XSZuQxtGaQl55gRXQDPnlfB2CtIrV44gHZOJ
|
32
|
+
88s+Ld9h44aoT2rWbLld6dU5ElZXWEJOim+aYKJewxX7PwEHn4iCpvMLu+4jXH3j
|
33
|
+
OkDTHheVJkxyhTDQ43ebg3/qi4yFaQyAHk3bQItwCw==
|
34
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,51 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
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
|
51
|
+
-----END RSA PRIVATE KEY-----
|
data/lib/arachni/http/request.rb
CHANGED
@@ -101,9 +101,6 @@ class Request < Message
|
|
101
101
|
# Maximum HTTP response size to accept, in bytes.
|
102
102
|
attr_accessor :response_max_size
|
103
103
|
|
104
|
-
# @private
|
105
|
-
attr_accessor :root_redirect_id
|
106
|
-
|
107
104
|
# @param [Hash] options
|
108
105
|
# Request options.
|
109
106
|
# @option options [String] :url
|
@@ -129,6 +126,7 @@ class Request < Message
|
|
129
126
|
super( options )
|
130
127
|
|
131
128
|
@train = false if @train.nil?
|
129
|
+
@fingerprint = true if @fingerprint.nil?
|
132
130
|
@update_cookies = false if @update_cookies.nil?
|
133
131
|
@follow_location = false if @follow_location.nil?
|
134
132
|
@max_redirects = (Options.http.request_redirect_limit || REDIRECT_LIMIT)
|
@@ -265,6 +263,13 @@ class Request < Message
|
|
265
263
|
!!@follow_location
|
266
264
|
end
|
267
265
|
|
266
|
+
# @return [Bool]
|
267
|
+
# `true` if the {Response} should be {Platform::Manager.fingerprint fingerprinted}
|
268
|
+
# for platforms, `false` otherwise.
|
269
|
+
def fingerprint?
|
270
|
+
@fingerprint
|
271
|
+
end
|
272
|
+
|
268
273
|
# @return [Bool]
|
269
274
|
# `true` if the {Response} should be analyzed by the {Trainer}
|
270
275
|
# for new elements, `false` otherwise.
|
@@ -343,6 +348,10 @@ class Request < Message
|
|
343
348
|
|
344
349
|
accept_encoding: 'gzip, deflate',
|
345
350
|
nosignal: true,
|
351
|
+
|
352
|
+
# If Content-Length is missing this option will have no effect, so
|
353
|
+
# we'll also stream the body to make sure that we can at least abort
|
354
|
+
# the reading of the response body if it exceeds this limit.
|
346
355
|
maxfilesize: max_size,
|
347
356
|
|
348
357
|
# Don't keep the socket alive if this is a blocking request because
|
@@ -384,17 +393,25 @@ class Request < Message
|
|
384
393
|
end
|
385
394
|
end
|
386
395
|
|
387
|
-
curl
|
388
|
-
|
396
|
+
curl = parsed_url.query ? url.gsub( "?#{parsed_url.query}", '' ) : url
|
397
|
+
typhoeus_request = Typhoeus::Request.new( curl, options )
|
389
398
|
|
390
399
|
if @on_complete.any?
|
391
|
-
|
400
|
+
response_body_buffer = ''
|
401
|
+
set_body_reader( typhoeus_request, response_body_buffer )
|
402
|
+
|
403
|
+
typhoeus_request.on_complete do |typhoeus_response|
|
404
|
+
if typhoeus_request.options[:maxfilesize]
|
405
|
+
typhoeus_response.options[:response_body] =
|
406
|
+
response_body_buffer
|
407
|
+
end
|
408
|
+
|
392
409
|
fill_in_data_from_typhoeus_response typhoeus_response
|
393
410
|
handle_response Response.from_typhoeus( typhoeus_response )
|
394
411
|
end
|
395
412
|
end
|
396
413
|
|
397
|
-
|
414
|
+
typhoeus_request
|
398
415
|
end
|
399
416
|
|
400
417
|
def to_h
|
@@ -480,33 +497,69 @@ class Request < Message
|
|
480
497
|
h
|
481
498
|
end
|
482
499
|
end
|
500
|
+
|
501
|
+
def encode( string )
|
502
|
+
@easy ||= Ethon::Easy.new( url: 'www.example.com' )
|
503
|
+
@easy.escape string
|
504
|
+
end
|
483
505
|
end
|
484
506
|
|
485
507
|
def prepare_headers
|
486
|
-
headers['Cookie'] = effective_cookies.
|
487
|
-
map { |k, v| "#{Cookie.encode( k )}=#{Cookie.encode( v )}" }.
|
488
|
-
join( ';' )
|
489
|
-
headers.delete( 'Cookie' ) if headers['Cookie'].empty?
|
490
|
-
|
491
508
|
headers['User-Agent'] ||= Options.http.user_agent
|
492
509
|
headers['Accept'] ||= 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
493
510
|
headers['From'] ||= Options.authorized_by if Options.authorized_by
|
494
511
|
|
495
512
|
headers.each { |k, v| headers[k] = Header.encode( v ) if v }
|
513
|
+
|
514
|
+
headers['Cookie'] = effective_cookies.
|
515
|
+
map { |k, v| "#{Cookie.encode( k )}=#{Cookie.encode( v )}" }.
|
516
|
+
join( ';' )
|
517
|
+
headers.delete( 'Cookie' ) if headers['Cookie'].empty?
|
518
|
+
|
519
|
+
headers
|
496
520
|
end
|
497
521
|
|
498
522
|
private
|
499
523
|
|
524
|
+
def client_run
|
525
|
+
typhoeus_request = to_typhoeus
|
526
|
+
|
527
|
+
response_body_buffer = ''
|
528
|
+
set_body_reader( typhoeus_request, response_body_buffer )
|
529
|
+
|
530
|
+
typhoeus_response = typhoeus_request.run
|
531
|
+
|
532
|
+
if typhoeus_request.options[:maxfilesize]
|
533
|
+
typhoeus_response.options[:response_body] = response_body_buffer
|
534
|
+
end
|
535
|
+
|
536
|
+
fill_in_data_from_typhoeus_response typhoeus_response
|
537
|
+
|
538
|
+
Response.from_typhoeus( typhoeus_response )
|
539
|
+
end
|
540
|
+
|
500
541
|
def fill_in_data_from_typhoeus_response( response )
|
501
|
-
|
502
|
-
|
542
|
+
# Only grab the last data.
|
543
|
+
# In case of CONNECT calls for HTTPS via proxy the first data will be
|
544
|
+
# the proxy-related stuff.
|
545
|
+
@headers_string = response.debug_info.header_out.last
|
546
|
+
@effective_body = response.debug_info.data_out.last
|
503
547
|
end
|
504
548
|
|
505
|
-
def
|
506
|
-
|
507
|
-
fill_in_data_from_typhoeus_response response
|
549
|
+
def set_body_reader( typhoeus_request, buffer )
|
550
|
+
return if !typhoeus_request.options[:maxfilesize]
|
508
551
|
|
509
|
-
|
552
|
+
aborted = nil
|
553
|
+
typhoeus_request.on_body do |chunk|
|
554
|
+
next aborted if aborted
|
555
|
+
|
556
|
+
if buffer.size >= typhoeus_request.options[:maxfilesize]
|
557
|
+
buffer.clear
|
558
|
+
next aborted = :abort
|
559
|
+
end
|
560
|
+
|
561
|
+
buffer << chunk
|
562
|
+
end
|
510
563
|
end
|
511
564
|
|
512
565
|
end
|