module Arachni::Element::Capabilities::Mutable
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Constants
- EXTRA_NAME
- FUZZ_NAME
- FUZZ_NAME_VALUE
- MUTATION_OPTIONS
Default formatting and mutation options.
Attributes
@return [String]
Name of the mutated parameter.
@return [String]
Original seed used for the {#mutations}.
Protected Class Methods
# File lib/arachni/element/capabilities/mutable.rb, line 335 def self.mutable_id( method, inputs, raw_inputs ) "#{method}:#{Arachni::Element::Capabilities::Inputtable.inputtable_id( inputs, raw_inputs )}" end
Public Instance Methods
@param [String] name
Sets the name of the fuzzed input.
# File lib/arachni/element/capabilities/mutable.rb, line 123 def affected_input_name=( name ) @affected_input_name = name.to_s end
@return [nil, String]
`nil` if no input has been fuzzed, the `String` value of the fuzzed input.
# 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
@param [String] value
Sets the value for the fuzzed input.
# File lib/arachni/element/capabilities/mutable.rb, line 117 def affected_input_value=( value ) self[affected_input_name] = value end
# File lib/arachni/element/capabilities/mutable.rb, line 321 def dup copy_mutable( super ) end
@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.
@param [String] payload
String to inject.
@param [Hash] options
{MUTATION_OPTIONS}
@yield [mutation]
Each generated mutation.
@yieldparam [Mutable]
@see immutables
# 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
@return [Set]
Names of input vectors to be excluded from {#mutations}.
# File lib/arachni/element/capabilities/mutable.rb, line 141 def immutables @immutables ||= Set.new end
# 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
@return [Bool]
`true` if the element has been mutated, `false` otherwise.
# File lib/arachni/element/capabilities/mutable.rb, line 135 def mutation? !!self.affected_input_name end
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.
@param [String] payload
The string to inject.
@param [Hash] opts
{MUTATION_OPTIONS}
@return [Array]
@see immutables
# File lib/arachni/element/capabilities/mutable.rb, line 274 def mutations( payload, opts = {} ) combo = [] each_mutation( payload, opts ) { |m| combo << m } combo end
# File lib/arachni/element/capabilities/mutable.rb, line 247 def parameter_name_audit? affected_input_name == FUZZ_NAME end
Resets the inputs to their original format/values.
# File lib/arachni/element/capabilities/mutable.rb, line 100 def reset super @affected_input_name = nil @seed = nil self end
@param [String] value
Sets the value for the fuzzed input.
# File lib/arachni/element/capabilities/mutable.rb, line 129 def seed=( value ) @seed = value.to_s end
# File lib/arachni/element/capabilities/mutable.rb, line 257 def switch_method self.dup.tap { |c| c.method = (c.method == :get ? :post : :get) } end
# 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
# File lib/arachni/element/capabilities/mutable.rb, line 315 def to_rpc_data d = super d.delete 'immutables' d end
# 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
@return [Boolean]
`true` if the mutation's {#affected_input_value} has been set to skip encoding, `false` otherwise.
# File lib/arachni/element/capabilities/mutable.rb, line 148 def with_raw_payload? raw_inputs.include? affected_input_name end
Protected Instance Methods
# File lib/arachni/element/capabilities/mutable.rb, line 327 def mutable_id Arachni::Element::Capabilities::Mutable.mutable_id( method, inputs, raw_inputs ) end
Private Instance Methods
# File lib/arachni/element/capabilities/mutable.rb, line 367 def copy_mutable( other ) if self.affected_input_name other.affected_input_name = self.affected_input_name.dup end other.seed = self.seed.dup if self.seed other.format = self.format # Carry over the immutables. other.immutables.merge self.immutables other end
# File lib/arachni/element/capabilities/mutable.rb, line 400 def create_and_yield_if_unique( list, inputs, seed, input_name, input_value,format, &block ) # We can check if it's unique prior to actually creating, so do it. return if list.include?( Arachni::Element::Capabilities::Mutable.mutable_id( self.method, inputs, [] ) ) element = create_mutation( inputs, seed, input_name, input_value, format ) return if !element yield_if_unique( element, list, &block ) element end
# File lib/arachni/element/capabilities/mutable.rb, line 380 def create_mutation( inputs, seed, input_name, input_value, format ) if !valid_input_value_data?( input_value ) print_debug_level_2 "Value not supported by #{audit_id}: #{input_value.inspect}" return end if !valid_input_name_data?( input_name ) print_debug_level_2 "Name not supported by #{audit_id}: #{input_name.inspect}" return end elem = self.dup.update( inputs ) elem.seed = seed elem.affected_input_name = input_name elem.affected_input_value = input_value elem.format = format elem end
# File lib/arachni/element/capabilities/mutable.rb, line 428 def each_formatted_payload( payload, formats, default_value = '' ) formats.each do |format| yield format, format_str( payload, format, default_value ) end end
Prepares an injection string following the specified formatting options as contained in the format bitfield.
@param [String] payload @param [String] default_str
Default value to be appended by the injection string if {Format::APPEND} is set in 'format'.
@param [Integer] format
Bitfield describing formatting preferences.
@return [String]
@see Format
# File lib/arachni/element/capabilities/mutable.rb, line 447 def format_str( payload, format, default_str = '' ) semicolon = null = append = nil null = "\0" if (format & Format::NULL) != 0 semicolon = ';' if (format & Format::SEMICOLON) != 0 append = default_str if (format & Format::APPEND) != 0 semicolon = append = null = '' if (format & Format::STRAIGHT) != 0 "#{semicolon}#{append}#{payload}#{null}" end
# File lib/arachni/element/capabilities/mutable.rb, line 341 def prepare_mutation_options( options ) options = MUTATION_OPTIONS.merge( options ) if options[:with_raw_payloads].nil? options[:with_raw_payloads] = Options.audit.with_raw_payloads? end if options[:parameter_values].nil? options[:parameter_values] = Options.audit.parameter_values? end if options[:parameter_names].nil? options[:parameter_names] = Options.audit.parameter_names? end if options[:with_extra_parameter].nil? options[:with_extra_parameter] = Options.audit.with_extra_parameter? end if options[:with_both_http_methods].nil? options[:with_both_http_methods] = Options.audit.with_both_http_methods? end options end
# File lib/arachni/element/capabilities/mutable.rb, line 458 def print_debug_formatting( opts ) return if !opts[:format] || !debug_level_2? print_debug_level_2 print_debug_level_2 'Formatting set to:' print_debug_level_2 '|' msg = [] opts[:format].each do |format| if( format & Format::NULL ) != 0 msg << 'null character termination (Format::NULL)' end if( format & Format::APPEND ) != 0 msg << 'append to default value (Format::APPEND)' end if( format & Format::STRAIGHT ) != 0 msg << 'straight, leave as is (Format::STRAIGHT)' end prep = "#{msg.join( ' and ' ).capitalize}. [Format mask: #{format}]" prep.gsub!( 'format::null', "Format::NULL [#{Format::NULL}]" ) prep.gsub!( 'format::append', "Format::APPEND [#{Format::APPEND}]" ) prep.gsub!( 'format::straight', "Format::STRAIGHT [#{Format::STRAIGHT}]" ) print_debug_level_2 "|----> #{prep}" msg.clear end nil end
# File lib/arachni/element/capabilities/mutable.rb, line 491 def print_debug_mutation( mutation ) return if !debug_level_2? print_debug_level_2 '|' print_debug_level_2 "|--> Auditing: #{mutation.affected_input_name}" print_debug_level_2 '|--> Inputs: ' mutation.inputs.each do |k, v| print_debug_level_2 "|----> #{k.inspect} => #{v.inspect}" end if mutation.raw_inputs.any? print_debug_level_2 '|--> Raw inputs: ' mutation.raw_inputs.each do |k| print_debug_level_2 "|----> #{k.inspect}" end end end
# File lib/arachni/element/capabilities/mutable.rb, line 510 def print_debug_trainer( opts ) print_debug_level_2 "Trainer set to: #{opts[:train] ? 'ON' : 'OFF'}" end
# File lib/arachni/element/capabilities/mutable.rb, line 419 def yield_if_unique( element, list ) return if list.include?( element.mutable_id ) print_debug_mutation element list << element yield element end