Class: Arachni::Plugins::WAFDetector

Inherits:
Arachni::Plugin::Base show all
Defined in:
components/plugins/waf_detector.rb

Overview

Web Application Firewall detection plugin.

This is a 4 stage process:

  1. Grab the original page as is
  2. Send a lot of innocent strings in non-existent inputs so as to profile normal behavior
  3. Send a lot of suspicious strings in non-existent inputs and check if behavior changes
  4. Make heads or tails of the gathered responses

Steps 1 to 3 will be repeated precision times and the responses will be averaged using rDiff analysis.

Author:

Version:

  • 0.1.3

Constant Summary collapse

STATUSES =
{
    inconclusive: %q{Could not establish a baseline behavior for the website.
        Due to that fact analysis has been aborted.},
    found:        %q{Request parameters are being filtered, this is usually a sign of a WAF.},
    not_found:    %q{Could not detect any sign of filtering, a WAF doesn't seem to be present.}
}

Constants included from Arachni

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

Instance Attribute Summary

Attributes inherited from Arachni::Plugin::Base

#framework, #options

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Arachni::Plugin::Base

#browser_cluster, distributable, distributable?, #framework_abort, #framework_pause, #framework_resume, gems, #http, #info, #initialize, is_distributable, merge, #register_results, #restore, #session, #suspend, #wait_while_framework_running, #with_browser

Methods inherited from Component::Base

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

Methods included from 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 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::Plugin::Base

Class Method Details

.infoObject



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
165
166
167
# File 'components/plugins/waf_detector.rb', line 139

def self.info
    {
        name:        'WAF Detector',
        description: %q{
Performs basic profiling on the web application in order to assess the existence
of a Web Application Firewall.

This is a 4 stage process:

1. Grab the original page as is.
2. Send a lot of innocent (vanilla) strings in non-existent inputs so as to
profile normal behavior.
3. Send a lot of suspicious (spicy) strings in non-existent inputs and check if
behavior changes.
4. Make heads or tails of the gathered responses.

Steps 1 to 3 will be repeated _precision_ times (default: 5) and the responses
will be averaged using rDiff analysis.
},
        author:      'Tasos "Zapotek" Laskos <[email protected]>',
        version:     '0.1.3',
        options:     [
            Options::Int.new( :precision,
                description: 'Stage precision (how many times to perform each detection stage).',
                default:     5
            )
        ]
    }
end

Instance Method Details

#clean_upObject



91
92
93
# File 'components/plugins/waf_detector.rb', line 91

def clean_up
    framework_resume
end

#foundObject



95
96
97
# File 'components/plugins/waf_detector.rb', line 95

def found
    log :found
end

#inconclusiveObject



103
104
105
# File 'components/plugins/waf_detector.rb', line 103

def inconclusive
    log :inconclusive
end

#log(status) ⇒ Object



107
108
109
110
# File 'components/plugins/waf_detector.rb', line 107

def log( status )
    print_ok STATUSES[status]
    register_results( 'status' => status.to_s, 'message' => STATUSES[status] )
end

#not_foundObject



99
100
101
# File 'components/plugins/waf_detector.rb', line 99

def not_found
    log :not_found
end

#prepareObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'components/plugins/waf_detector.rb', line 30

def prepare
    @precision = options[:precision]

    bad = [
        '../../../../',
        '<script>foo</script>',
        '\'--;`',
    ]

    names = []
    bad.size.times do |i|
        names << i.to_s + '_' + Digest::SHA2.hexdigest( rand( i * 1000 ).to_s )
    end

    @safe   = { }
    @unsafe = { }
    names.each.with_index do |name, i|
        @safe[name]   = 'value_' + name
        @unsafe[name] = i.to_s + '_' + bad.join( '_' )
    end

    @url = framework.options.url

    @responses = {
        original: nil,
        vanilla:  nil,
        spicy:    nil
    }

    framework_pause
end

#queue_originalObject



112
113
114
115
116
117
118
119
# File 'components/plugins/waf_detector.rb', line 112

def queue_original
    @precision.times do
        http.get( @url.to_s ) do |res|
            @responses[:original] ||= res.body
            @responses[:original] = @responses[:original].rdiff( res.body )
        end
    end
end

#queue_spicyObject



130
131
132
133
134
135
136
137
# File 'components/plugins/waf_detector.rb', line 130

def queue_spicy
    @precision.times do
        http.get( @url.to_s, parameters: @unsafe ) do |res|
            @responses[:spicy] ||= res.body
            @responses[:spicy] = @responses[:spicy].rdiff( res.body )
        end
    end
end

#queue_vanillaObject



121
122
123
124
125
126
127
128
# File 'components/plugins/waf_detector.rb', line 121

def queue_vanilla
    @precision.times do
        http.get( @url.to_s, parameters: @safe ) do |res|
            @responses[:vanilla] ||= res.body
            @responses[:vanilla] = @responses[:vanilla].rdiff( res.body )
        end
    end
end

#runObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'components/plugins/waf_detector.rb', line 62

def run
    print_status "Starting detection with a precision of #{@precision}."

    print_status 'Stage #1: Requesting original page.'
    queue_original

    print_status 'Stage #2: Requesting with vanilla inputs.'
    queue_vanilla

    print_status 'Stage #3: Requesting with spicy inputs.'
    queue_spicy

    print_status 'Stage #4: Analyzing gathered responses.'

    http.after_run do
        if @responses[:original] == @responses[:vanilla]
            if @responses[:vanilla] == @responses[:spicy]
                not_found
            else
                found
            end
        else
            inconclusive
        end
    end

    http.run
end