Class: Arachni::Checks::CSRF

Inherits:
Arachni::Check::Base show all
Defined in:
components/checks/active/csrf.rb

Overview

Cross-Site Request Forgery check.

It uses 4-pass reverse-diff analysis to determine which forms affect business logic and audits them for CSRF.

Using this technique noise/false-positives should be kept to a minimum, however we do need to jump through some hoops.

The technique used to identify which forms are CSRF worthy is described bellow.

=== 4-pass rDiff CSRF detection:

  • Request each page without cookies
    • Extract forms.
  • Request each page with cookies
    • Extract forms.
  • Check forms that appear only when logged-in for CSRF.

In order for the check to give meaningful results, a valid session of a logged-in user must be supplied to the framework.

However, as Arachni goes through the system it will gather cookies just like a user would, so if there are forms that only appear after a guest has performed a previous event it will check these too.

Constant Summary

Constants included from Arachni::Check::Auditor

Arachni::Check::Auditor::DOM_ELEMENTS_WITH_INPUTS, Arachni::Check::Auditor::ELEMENTS_WITH_INPUTS, Arachni::Check::Auditor::FILE_SIGNATURES, Arachni::Check::Auditor::FILE_SIGNATURES_PER_PLATFORM, Arachni::Check::Auditor::Format, Arachni::Check::Auditor::SOURCE_CODE_SIGNATURES_PER_PLATFORM

Constants included from Arachni

BANNER, Arachni::Cookie, Form, Header, JSON, Link, LinkTemplate, NestedCookie, Severity, UIForm, UIInput, VERSION, WEBSITE, WIKI, XML

Instance Attribute Summary

Attributes included from Arachni::Check::Auditor

#framework, #page

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Arachni::Check::Base

#browser_cluster, #clean_up, elements, exempt_platforms, has_exempt_platforms?, has_platforms?, #initialize, platforms, #plugins, prefer, #preferred, preferred, #prepare, #session, supports_platforms?

Methods included from Arachni::Check::Auditor

#audit, #audit_differential, #audit_signature, #audit_timeout, #audited, #audited?, #buffered_audit, #each_candidate_dom_element, #each_candidate_element, has_timeout_candidates?, #http, #initialize, #log, #log_issue, #log_remote_file, #log_remote_file_if_exists, #match_and_log, #max_issues, #preferred, reset, #skip?, timeout_audit_run, #trace_taint, #with_browser, #with_browser_cluster

Methods inherited from Arachni::Component::Base

author, description, fullname, #shortname, shortname, shortname=, version

Methods included from Arachni::Component::Output

#depersonalize_output, #depersonalize_output?, #intercept_print_message

Methods included from UI::Output

#caller_location, #debug?, #debug_level, #debug_level_1?, #debug_level_2?, #debug_level_3?, #debug_level_4?, #debug_off, #debug_on, #disable_only_positives, #error_buffer, #error_log_fd, #error_logfile, #has_error_log?, #included, #log_error, #mute, #muted?, #only_positives, #only_positives?, #print_bad, #print_debug, #print_debug_backtrace, #print_debug_exception, #print_debug_level_1, #print_debug_level_2, #print_debug_level_3, #print_debug_level_4, #print_error, #print_error_backtrace, #print_exception, #print_info, #print_line, #print_ok, #print_status, #print_verbose, #reroute_to_file, #reroute_to_file?, reset_output_options, #set_error_logfile, #unmute, #verbose?, #verbose_off, #verbose_on

Methods included from Arachni::Component::Utilities

#read_file

Methods included from Utilities

#available_port, available_port_mutex, #bytes_to_kilobytes, #bytes_to_megabytes, #caller_name, #caller_path, #cookie_decode, #cookie_encode, #cookies_from_file, #cookies_from_parser, #cookies_from_response, #exception_jail, #exclude_path?, #follow_protocol?, #form_decode, #form_encode, #forms_from_parser, #forms_from_response, #full_and_absolute_url?, #generate_token, #get_path, #hms_to_seconds, #html_decode, #html_encode, #include_path?, #links_from_parser, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_set_cookie, #path_in_domain?, #path_too_deep?, #port_available?, #rand_port, #random_seed, #redundant_path?, #regexp_array_match, #remove_constants, #request_parse_body, #seconds_to_hms, #skip_page?, #skip_path?, #skip_resource?, #skip_response?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parse_query, #uri_parser, #uri_rewrite

Methods included from Arachni

URI, collect_young_objects, #get_long_win32_filename, jruby?, null_device, profile?, windows?

Constructor Details

This class inherits a constructor from Arachni::Check::Base

Class Method Details

.infoObject


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'components/checks/active/csrf.rb', line 92

def self.info
    {
        name:        'CSRF',
        description: %q{
It uses differential analysis to determine which forms affect business logic and
checks them for lack of anti-CSRF tokens.

(Works best with a valid session.)
},
        elements:    [ Element::Form ],
        author:      'Tasos "Zapotek" Laskos <[email protected]> ',
        version:     '0.4.1',

        issue:       {
            name:            %q{Cross-Site Request Forgery},
            description:     %q{
In the majority of today's web applications, clients are required to submit forms
which can perform sensitive operations.

An example of such a form being used would be when an administrator wishes to
create a new user for the application.

In the simplest version of the form, the administrator would fill-in:

* Name
* Password
* Role (level of access)

Continuing with this example, Cross Site Request Forgery (CSRF) would occur when
the administrator is tricked into clicking on a link, which if logged into the
application, would automatically submit the form without any further interaction.

Cyber-criminals will look for sites where sensitive functions are performed in
this manner and then craft malicious requests that will be used against clients
via a social engineering attack.

There are 3 things that are required for a CSRF attack to occur:

1. The form must perform some sort of sensitive action.
2. The victim (the administrator the example above) must have an active session.
3. Most importantly, all parameter values must be **known** or **guessable**.

Arachni discovered that all parameters within the form were known or predictable
and therefore the form could be vulnerable to CSRF.

_Manual verification may be required to check whether the submission will then
perform a sensitive action, such as reset a password, modify user profiles, post
content on a forum, etc._
},
            references:  {
                'Wikipedia'    => 'http://en.wikipedia.org/wiki/Cross-site_request_forgery',
                'OWASP'        => 'https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)',
                'CGI Security' => 'http://www.cgisecurity.com/csrf-faq.html'
            },
            tags:            %w(csrf rdiff form token),
            cwe:             352,
            severity:        Severity::HIGH,
            remedy_guidance: %q{
Based on the risk (determined by manual verification) of whether the form submission
performs a sensitive action, the addition of anti-CSRF tokens may be required.

These tokens can be configured in such a way that each session generates a new
anti-CSRF token or such that each individual request requires a new token.

It is important that the server track and maintain the status of each token (in
order to reject requests accompanied by invalid ones) and therefore prevent
cyber-criminals from knowing, guessing or reusing them.

_For examples of framework specific remediation options, please refer to the references._
}
        }
    }
end

Instance Method Details

#log_form(form) ⇒ Object


77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'components/checks/active/csrf.rb', line 77

def log_form( form )
    url  = form.action
    name = form.name_or_id

    if audited?( "#{url}::#{name}" )
        print_info "Skipping already audited form '#{name}' at '#{page.url}'"
        return
    end

    audited( "#{url}::#{name}" )

    log( vector: form, proof: form.source )
    print_ok "Found unprotected form with name '#{name}' at '#{page.url}'"
end

#runObject


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'components/checks/active/csrf.rb', line 42

def run
    # TODO:
    #   Check forms that were created dynamically via JS.
    #   Maybe add a csrf_dom check?
    return if page.dom.transitions.any?

    print_status 'Looking for CSRF candidates...'
    print_status 'Simulating logged-out user.'

    # request page without cookies, simulating a logged-out user
    http.get( page.url, cookies: {}, no_cookie_jar: true ) do |res|
        # extract forms from the body of the response
        logged_out = forms_from_response( res ).reject { |f| f.inputs.empty? }

        print_status "Found #{logged_out.size} context irrelevant forms."

        # get forms that are worthy of testing for CSRF i.e. appear only
        # when the user is logged-in
        candidates = page.forms - logged_out

        print_status "Found #{candidates.size} CSRF candidates."

        candidates.each do |form|
            # If the form has no source then it was dynamically provided by
            # some component, so skip it.
            next if !form.source

            # If a form has a nonce then we're cool.
            next if form.has_nonce?

            log_form( form )
        end
    end
end