Module: Arachni::Element::Capabilities::Analyzable::Differential

Included in:
Arachni::Element::Capabilities::Analyzable
Defined in:
lib/arachni/element/capabilities/analyzable/differential.rb

Overview

Performs boolean injection and behavioral analysis (using differential analysis techniques based on Support::Signature comparisons) in order to determine whether the web application is responding to the injected data and how.

If the behavior can be manipulated by the injected data in ways that it's not supposed to (like when evaluating injected code) then the element is deemed vulnerable.

Author:

  • Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>

Constant Summary collapse

DIFFERENTIAL_OPTIONS =
{
    format:         [Arachni::Element::Capabilities::Mutable::Format::STRAIGHT],

    # Amount of refinement operations to remove context-irrelevant dynamic
    # content -- like banners etc.
    precision:      2,

    # Override global fuzzing settings and only use the default method of
    # the element under audit.
    with_raw_payloads:      false,
    with_both_http_methods: false,
    parameter_names:        false,
    with_extra_parameter:   false,

    # Disable {Arachni::Options#audit_cookies_extensively}, there's little
    # to be gained in this case and just causes interference.
    extensively:    false,

    # Don't generate or submit any mutations with default or sample inputs.
    skip_original:  true,

    # Allow redundant audits, we need multiple ones for noise-reduction.
    redundant:      true,

    # Don't let #audit print output, we'll handle that ourselves.
    silent:         true,

    # Default value for a forceful 'false' response.
    false:          '-1'
}
DIFFERENTIAL_ALLOWED_STATUS =
Set.new([200, 404])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#differential_analysis_optionsObject

Returns the value of attribute differential_analysis_options.



65
66
67
# File 'lib/arachni/element/capabilities/analyzable/differential.rb', line 65

def differential_analysis_options
  @differential_analysis_options
end

Class Method Details

.resetObject



27
28
29
# File 'lib/arachni/element/capabilities/analyzable/differential.rb', line 27

def reset
    # In case we want to reset state or something...
end

Instance Method Details

#differential_analysis(opts = {}) ⇒ Bool

Performs differential analysis and logs an issue should there be one.

opts = {
    false: 'false resource id',
    pairs: [
          { 'true expression' => 'false expression' }
    ]
}

element.differential_analysis( opts )

Here's how it goes:

  • let `control` be the response of the injection of 'false resource id'

  • let `true_response` be the response of the injection of 'true expression'

  • let `false_response` be the response of the injection of 'false expression'

  • let `control_verification` be a fresh control

A vulnerability is logged if:

control == control_verification && control == false_response AND
  true_response.code == 200 AND false_response != true_response

The `bool` response is also checked in order to determine if it's a custom 404, if it is then it'll be skipped.

If a block has been provided analysis and logging will be delegated to it.

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :format (Integer)

    As seen in Mutable::Format.

  • :precision (Integer)

    Amount of refinement iterations to perform for the signatures.

  • :pairs (Array<Hash>)

    Pair of strings that should yield different results when interpreted. Keys should be the `true` expressions.

  • :false (String)

    A string which would illicit a 'false' response but without any code.

Returns:

  • (Bool)

    `true` if the audit was scheduled successfully, `false` otherwise (like if the resource is out of scope or already audited).



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
165
166
167
168
169
170
171
172
# File 'lib/arachni/element/capabilities/analyzable/differential.rb', line 109

def differential_analysis( opts = {} )
    return if self.inputs.empty?

    with_missing_values = Set.new( self.inputs.select { |k, v| v.to_s.empty? }.keys )
    if self.inputs.size > 1 && self.inputs.size == with_missing_values.size
        print_debug 'Differential analysis: Inputs are missing default values.'
        return false
    end

    return false if audited? audit_id
    audited audit_id

    if scope.out?
        print_debug 'Differential analysis: Element is out of scope,' <<
                        " skipping: #{audit_id}"
        return false
    end

    @differential_analysis_options = opts.dup
    opts = self.class::MUTATION_OPTIONS.merge( DIFFERENTIAL_OPTIONS.merge( opts ) )
    opts[:skip_like] = proc do |mutation|
        self.inputs.size > 1 &&
            with_missing_values.include?( mutation.affected_input_name )
    end

    mutations_size = 0
    each_mutation( opts[:false], opts ) { mutations_size += 1 }

    @data_gathering = {
        mutations_size:     mutations_size,
        expected_responses: mutations_size + (mutations_size * opts[:pairs].size * 2),
        received_responses: 0,
        done:               false,
        controls:           {}
    }

    # Holds all the data from the probes.
    @signatures = {
        # Control baseline per input.
        controls:              {},

        # Verification control baseline per input.
        controls_verification: {},

        # Corrupted baselines per input.
        corrupted:             {},

        # Rest of the data are dynamically populated using input pairs
        # as keys.
    }

    # Populate the baseline/control forced-false signatures.
    populate_control_signatures( opts )

    http.after_run do
        # Populate the 'true' signatures.
        populate_signatures( :true, opts )

        # Populate the 'false' signatures.
        populate_signatures( :false, opts )
    end

    true
end

#dupObject



174
175
176
177
178
179
180
# File 'lib/arachni/element/capabilities/analyzable/differential.rb', line 174

def dup
    e = super
    return e if !@differential_analysis_options

    e.differential_analysis_options = @differential_analysis_options.dup
    e
end

#to_rpc_dataObject



182
183
184
# File 'lib/arachni/element/capabilities/analyzable/differential.rb', line 182

def to_rpc_data
    super.tap { |data| data.delete 'differential_analysis_options' }
end