Module: Arachni::Element::Capabilities::Mutable

Overview

Author:

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

Defined Under Namespace

Modules: Format

Constant Summary collapse

MUTATION_OPTIONS =

Default formatting and mutation options.

{

    # Formatting of the injection strings.
    #
    # A new set of audit inputs will be generated for each value in the array.
    #
    # Values can be OR'ed bitfields of all available constants of {Format}.
    format:                 [
        Format::STRAIGHT, Format::APPEND,
        Format::NULL, Format::APPEND | Format::NULL
    ],

    # Inject the payload into parameter values.
    #
    # * `nil`: Use system settings (`!Options.audit.parameter_values`).
    # * `true`
    # * `false`
    parameter_values:       nil,

    # Place the payload as an input name.
    #
    # * `nil`: Use system settings (`!Options.audit.parameter_names`).
    # * `true`
    # * `false`
    parameter_names:        nil,

    # Add mutations with non-encoded payloads.
    #
    # * `nil`: Use system settings (`!Options.audit.with_raw_parameters`).
    # * `true`
    # * `false`
    with_raw_payloads:       nil,

    # Add the payload to an extra parameter.
    #
    # * `nil`: Use system settings (`!Options.audit.with_extra_parameter`).
    # * `true`
    # * `false`
    with_extra_parameter:   nil,

    # `nil`:   Use system settings (`!Options.audit.with_both_http_methods`).
    # `true`:  Don't create mutations with other methods (GET/POST).
    # `false`: Create mutations with other methods (GET/POST).
    with_both_http_methods: nil,

    # Array of parameter names remain untouched.
    skip:                   []
}
EXTRA_NAME =
'extra_arachni_input'
FUZZ_NAME =
'Parameter name fuzzing'
FUZZ_NAME_VALUE =
'arachni_name_fuzz'

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#affected_input_nameString

Returns Name of the mutated parameter.

Returns:

  • (String)

    Name of the mutated parameter.



17
18
19
# File 'lib/arachni/element/capabilities/mutable.rb', line 17

def affected_input_name
  @affected_input_name
end

#formatObject

Returns the value of attribute format.



23
24
25
# File 'lib/arachni/element/capabilities/mutable.rb', line 23

def format
  @format
end

#seedString

Returns Original seed used for the #mutations.

Returns:



21
22
23
# File 'lib/arachni/element/capabilities/mutable.rb', line 21

def seed
  @seed
end

Instance Method Details

#affected_input_valuenil, String

Returns `nil` if no input has been fuzzed, the `String` value of the fuzzed input.

Returns:

  • (nil, String)

    `nil` if no input has been fuzzed, the `String` value of the fuzzed input.



110
111
112
113
# File 'lib/arachni/element/capabilities/mutable.rb', line 110

def affected_input_value
    return if !affected_input_name
    self[affected_input_name].to_s
end

#affected_input_value=(value) ⇒ Object

Parameters:

  • value (String)

    Sets the value for the fuzzed input.



117
118
119
# File 'lib/arachni/element/capabilities/mutable.rb', line 117

def affected_input_value=( value )
    self[affected_input_name] = value
end

#dupObject



321
322
323
# File 'lib/arachni/element/capabilities/mutable.rb', line 321

def dup
    copy_mutable( super )
end

#each_mutation(payload, options = {}) {|mutation| ... } ⇒ Object

Note:

Vector names in #immutables will be excluded.

Injects the `payload` in self's values according to formatting options and returns an array of mutations of self.

Parameters:

Yields:

  • (mutation)

    Each generated mutation.

Yield Parameters:

See Also:



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/arachni/element/capabilities/mutable.rb', line 167

def each_mutation( payload, options = {}, &block )
    return if self.inputs.empty?

    if !valid_input_data?( payload )
        print_debug_level_2 "Payload not supported by #{self}: #{payload.inspect}"
        return
    end

    print_debug_trainer( options )
    print_debug_formatting( options )

    options          = prepare_mutation_options( options )
    generated        = Support::LookUp::HashSet.new
    filled_in_inputs = Options.input.fill( @inputs )

    if options[:parameter_values]
        @inputs.keys.each do |name|
            # Don't let parameter name pollution from an old audit of an
            # input name trick us into doing the same for elements without
            # that option.
            next if name == EXTRA_NAME
            next if immutables.include?( name )

            each_formatted_payload(
                payload, options[:format], filled_in_inputs[name]
            ) do |format, formatted_payload|

                elem = create_and_yield_if_unique(
                    generated, filled_in_inputs, payload, name,
                    formatted_payload, format, &block
                )

                next if !elem

                if options[:with_raw_payloads]
                    yield_if_unique( elem.with_raw_payload, generated, &block )
                end

                if options[:with_both_http_methods]
                    yield_if_unique( elem.switch_method, generated, &block )
                end
            end
        end
    end

    if options[:with_extra_parameter]
        if valid_input_name?( EXTRA_NAME )
            each_formatted_payload( payload, options[:format] ) do |format, formatted_payload|

                elem = create_and_yield_if_unique(
                    generated, filled_in_inputs.merge( EXTRA_NAME => '' ),
                    payload, EXTRA_NAME, formatted_payload, format, &block
                )

                next if !elem || !options[:with_both_http_methods]
                yield_if_unique( elem.switch_method, generated, &block )
            end
        else
            print_debug_level_2 'Extra name not supported as input name by' <<
                                    " #{audit_id}: #{payload.inspect}"
        end
    end

    if options[:parameter_names]
        if valid_input_name_data?( payload )
            elem                     = self.dup.update( filled_in_inputs )
            elem.affected_input_name = FUZZ_NAME
            elem[payload]            = FUZZ_NAME_VALUE
            elem.seed                = payload

            yield_if_unique( elem, generated, &block )
        else
            print_debug_level_2 'Payload not supported as input name by' <<
                                    " #{audit_id}: #{payload.inspect}"
        end
    end

    nil
end

#immutablesSet

Returns Names of input vectors to be excluded from #mutations.

Returns:

  • (Set)

    Names of input vectors to be excluded from #mutations.



141
142
143
# File 'lib/arachni/element/capabilities/mutable.rb', line 141

def immutables
    @immutables ||= Set.new
end

#inspectObject



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/arachni/element/capabilities/mutable.rb', line 292

def inspect
    s = "#<#{self.class} (#{http_method}) "

    if !orphan?
        s << "auditor=#{auditor.class} "
    end

    s << "url=#{url.inspect} "
    s << "action=#{action.inspect} "

    s << "default-inputs=#{default_inputs.inspect} "
    s << "inputs=#{inputs.inspect} "
    s << "raw_inputs=#{raw_inputs.inspect} "

    if mutation?
        s << "seed=#{seed.inspect} "
        s << "affected-input-name=#{affected_input_name.inspect} "
        s << "affected-input-value=#{affected_input_value.inspect}"
    end

    s << '>'
end

#mutation?Bool

Returns `true` if the element has been mutated, `false` otherwise.

Returns:

  • (Bool)

    `true` if the element has been mutated, `false` otherwise.



135
136
137
# File 'lib/arachni/element/capabilities/mutable.rb', line 135

def mutation?
    !!self.affected_input_name
end

#mutations(payload, opts = {}) ⇒ Array

Injects the `payload` in self's values according to formatting options and returns an array of mutations of self.

Vector names in #immutables will be excluded.

Parameters:

Returns:

See Also:



274
275
276
277
278
# File 'lib/arachni/element/capabilities/mutable.rb', line 274

def mutations( payload, opts = {} )
    combo = []
    each_mutation( payload, opts ) { |m| combo << m }
    combo
end

#parameter_name_audit?Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/arachni/element/capabilities/mutable.rb', line 247

def parameter_name_audit?
    affected_input_name == FUZZ_NAME
end

#resetObject

Resets the inputs to their original format/values.



100
101
102
103
104
105
# File 'lib/arachni/element/capabilities/mutable.rb', line 100

def reset
    super
    @affected_input_name = nil
    @seed                = nil
    self
end

#switch_methodObject



257
258
259
# File 'lib/arachni/element/capabilities/mutable.rb', line 257

def switch_method
    self.dup.tap { |c| c.method = (c.method == :get ? :post : :get) }
end

#to_hObject



280
281
282
283
284
285
286
287
288
289
290
# File 'lib/arachni/element/capabilities/mutable.rb', line 280

def to_h
    h = super

    if mutation?
        h[:affected_input_name]  = self.affected_input_name
        h[:affected_input_value] = self.affected_input_value
        h[:seed]                 = self.seed
    end

    h
end

#to_rpc_dataObject



315
316
317
318
319
# File 'lib/arachni/element/capabilities/mutable.rb', line 315

def to_rpc_data
    d = super
    d.delete 'immutables'
    d
end

#with_raw_payloadObject



251
252
253
254
255
# File 'lib/arachni/element/capabilities/mutable.rb', line 251

def with_raw_payload
    self.dup.tap do |c|
        c.raw_inputs << c.affected_input_name
    end
end

#with_raw_payload?Boolean

Returns `true` if the mutation's #affected_input_value has been set to skip encoding, `false` otherwise.

Returns:

  • (Boolean)

    `true` if the mutation's #affected_input_value has been set to skip encoding, `false` otherwise.



148
149
150
# File 'lib/arachni/element/capabilities/mutable.rb', line 148

def with_raw_payload?
    raw_inputs.include? affected_input_name
end