Class: Arachni::Element::Form

Inherits:
Base show all
Includes:
Capabilities::Refreshable
Defined in:
lib/arachni/element/form.rb

Constant Summary collapse

ORIGINAL_VALUES =
'__original_values__'
SAMPLE_VALUES =
'__sample_values__'

Constants included from Capabilities::Auditable

Capabilities::Auditable::OPTIONS

Constants included from Capabilities::Auditable::RDiff

Capabilities::Auditable::RDiff::RDIFF_OPTIONS

Constants included from Capabilities::Auditable::Taint

Capabilities::Auditable::Taint::TAINT_OPTIONS

Constants included from Capabilities::Mutable

Capabilities::Mutable::MUTATION_OPTIONS

Instance Attribute Summary collapse

Attributes inherited from Base

#raw

Attributes included from Capabilities::Auditable

#auditor, #opts, #orig

Attributes included from Capabilities::Mutable

#altered

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Capabilities::Refreshable

#refresh

Methods inherited from Base

#action, #action=, #method, #method=, #url, #url=

Methods included from Utilities

#cookie_encode, #cookies_from_document, #cookies_from_file, #cookies_from_response, #exception_jail, #exclude_path?, #extract_domain, #form_decode, #form_encode, #form_parse_request_body, #forms_from_document, #forms_from_response, #get_path, #hash_keys_to_str, #html_decode, #html_encode, #include_path?, #links_from_document, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_query, #parse_set_cookie, #parse_url_vars, #path_in_domain?, #path_too_deep?, #remove_constants, #seed, #skip_path?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parser, #url_sanitize

Methods included from Capabilities::Auditable

#==, #[], #[]=, #audit, #auditable, #auditable=, #changes, #debug?, #has_inputs?, #hash, #http, #info, #orphan?, #override_instance_scope, #override_instance_scope?, #print_bad, #print_debug, #print_debug_backtrace, #print_error, #print_error_backtrace, #print_info, #print_line, #print_ok, #print_status, #provisioned_issue_id, #remove_auditor, #reset, reset, reset_instance_scope, #reset_scope_override, restrict_to_elements, #scope_audit_id, #skip_path?, #status_string, #submit, #update

Methods included from Capabilities::Auditable::RDiff

included, #rdiff_analysis

Methods included from Capabilities::Auditable::Timeout

add_timeout_audit_block, add_timeout_candidate, #call_on_timing_blocks, call_on_timing_blocks, current_timeout_audit_operations_cnt, included, on_timing_attacks, #responsive?, running_timeout_attacks?, #timeout_analysis, timeout_analysis_phase_2, timeout_audit_blocks, timeout_audit_operations_cnt, timeout_audit_run, timeout_loaded_modules

Methods included from Capabilities::Auditable::Taint

#taint_analysis

Methods included from Capabilities::Mutable

#altered_value, #altered_value=, #immutables, #mutated?, #mutations_for, #permutations, #permutations_for

Constructor Details

#initialize(url, raw = {}) ⇒ Form

Creates a new Form element from a URL and auditable data.

Parameters:

  • url (String)

    owner URL – URL of the page which contained the

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

    If empty, the element will be initialized without auditable inputs.

    If a full Hash is passed, it will look for an actionable URL String in the following keys:

    • ‘href’

    • :href

    • ‘action’

    • :action

    For an method String in:

    • ‘method’

    • :method

    Method defaults to ‘get’.

    For an auditable inputs Hash in:

    • ‘inputs’

    • :inputs

    • ‘auditable’

    these should contain inputs in name=>value pairs.



60
61
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
# File 'lib/arachni/element/form.rb', line 60

def initialize( url, raw = {} )
    super( url, raw )

    was_opts_hash = false
    begin
        self.action = @raw['action'] || @raw[:action] || @raw['attrs']['action']
        was_opts_hash = true
    rescue
        self.action = self.url
    end

    begin
        self.method = @raw['method'] || @raw[:method] || @raw['attrs']['method']
        was_opts_hash = true
    rescue
        self.method = 'get'
    end

    if !was_opts_hash && (@raw.keys & [:inputs, 'inputs', 'auditable']).empty?
        self.auditable = @raw
    else
        self.auditable = @raw[:inputs] || @raw['inputs'] || simple['auditable']
    end

    self.auditable ||= {}

    @orig = self.auditable.dup
    @orig.freeze
end

Instance Attribute Details

#nonce_nameString

Returns the name of the input name that holds the nonce.

Returns:

  • (String)

    the name of the input name that holds the nonce



31
32
33
# File 'lib/arachni/element/form.rb', line 31

def nonce_name
  @nonce_name
end

Class Method Details

.decode(str) ⇒ String

Decodes a String encoded for an HTTP request’s body.

Examples:

p Form.decode "%2B%25+%3B%26%5C%3D%5C0"
#=> "+% ;&\\=\\0"

Parameters:

Returns:

  • (String)

    the decoded string



1218
1219
1220
# File 'lib/arachni/element/form.rb', line 1218

def self.decode( str )
    URI.decode( str.to_s.recode.gsub( '+', ' ' ) )
end

.encode(str) ⇒ String

Encodes a String‘s reserved characters in order to prepare it to be included in a request body.

#example

p Form.encode "+% ;&\\=\0"
#=> "%2B%25+%3B%26%5C%3D%00"

Parameters:

Returns:

  • (String)

    the encoded string



1199
1200
1201
# File 'lib/arachni/element/form.rb', line 1199

def self.encode( str )
    ::URI.encode( ::URI.encode( str, '+%' ).recode.gsub( ' ', '+' ), ";&\\=\0" )
end

.from_document(url, document) ⇒ Array<Form>

Returns an array of forms from an HTML document.

Examples:

html_form = "<form action='/submit'>\n    <input type='text' name='text-input' />\n</form>\n"

ap Form.from_document( 'http://stuff.com', html_form ).first
#=> #<Arachni::Element::Form:0x03123600
#    attr_accessor :action = "http://stuff.com/submit",
#    attr_accessor :auditable = {
#        "text-input" => ""
#    },
#    attr_accessor :method = "get",
#    attr_accessor :url = "http://stuff.com/",
#    attr_reader :hash = 3370163854416367834,
#    attr_reader :opts = {},
#    attr_reader :orig = {
#        "text-input" => ""
#    },
#    attr_reader :raw = {
#            "attrs" => {
#            "action" => "http://stuff.com/submit",
#            "method" => "get"
#        },
#         "textarea" => [],
#            "input" => [
#            [0] {
#                "type" => "text",
#                "name" => "text-input"
#            }
#        ],
#           "select" => [],
#        "auditable" => [
#            [0] {
#                "type" => "text",
#                "name" => "text-input"
#            }
#        ]
#    }
#>

ap Form.from_document( 'http://stuff.com', Nokogiri::HTML( html_form ) ).first
#=> #<Arachni::Element::Form:0x03123600
#    attr_accessor :action = "http://stuff.com/submit",
#    attr_accessor :auditable = {
#        "text-input" => ""
#    },
#    attr_accessor :method = "get",
#    attr_accessor :url = "http://stuff.com/",
#    attr_reader :hash = 3370163854416367834,
#    attr_reader :opts = {},
#    attr_reader :orig = {
#        "text-input" => ""
#    },
#    attr_reader :raw = {
#            "attrs" => {
#            "action" => "http://stuff.com/submit",
#            "method" => "get"
#        },
#         "textarea" => [],
#            "input" => [
#            [0] {
#                "type" => "text",
#                "name" => "text-input"
#            }
#        ],
#           "select" => [],
#        "auditable" => [
#            [0] {
#                "type" => "text",
#                "name" => "text-input"
#            }
#        ]
#    }
#>

Parameters:

  • url (String)

    request URL

  • document (String, Nokogiri::HTML::Document)

Returns:



1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
# File 'lib/arachni/element/form.rb', line 1143

def self.from_document( url, document )
    document = Nokogiri::HTML( document.to_s ) if !document.is_a?( Nokogiri::HTML::Document )
    base_url = url
    begin
        base_url = document.search( '//base[@href]' )[0]['href']
    rescue
        base_url = url
    end
    document.search( '//form' ).map do |form|
        next if !(form = form_from_element( base_url, form ))
        form.url = url
        form
    end.compact
end

.from_response(response) ⇒ Array<Form>

Returns an array of forms by parsing the body of an HTTP response.

Examples:

body = "   <form action='/submit'>\n        <input type='text' name='text-input' />\n   </form>\n"

res = Typhoeus::Response.new( effective_url: 'http://host', body: body )

ap Form.from_response( res ).first
#=> #<Arachni::Element::Form:0x017c7788
#    attr_accessor :action = "http://host/submit",
#    attr_accessor :auditable = {
#        "text-input" => ""
#    },
#    attr_accessor :method = "get",
#    attr_accessor :url = "http://host/",
#    attr_reader :hash = 343244616730070569,
#    attr_reader :opts = {},
#    attr_reader :orig = {
#        "text-input" => ""
#    },
#    attr_reader :raw = {
#            "attrs" => {
#            "action" => "http://host/submit",
#            "method" => "get"
#        },
#         "textarea" => [],
#            "input" => [
#            [0] {
#                "type" => "text",
#                "name" => "text-input"
#            }
#        ],
#           "select" => [],
#        "auditable" => [
#            [0] {
#                "type" => "text",
#                "name" => "text-input"
#            }
#        ]
#    }
#>

Parameters:

Returns:



1053
1054
1055
# File 'lib/arachni/element/form.rb', line 1053

def self.from_response( response )
    from_document( response.effective_url, response.body )
end

.parse_request_body(body) ⇒ Hash

Parses an HTTP request body generated by submitting a form.

Examples:

Simple

p Form.parse_request_body 'first_name=John&last_name=Doe'
#=> {"first_name"=>"John", "last_name"=>"Doe"}

Weird

body = 'testID=53738&deliveryID=53618&testIDs=&deliveryIDs=&selectedRows=2' +
    '&event=&section=&event%3Dmanage%26amp%3Bsection%3Dexam=Manage+selected+exam'
p Form.parse_request_body body
#=> {"testID"=>"53738", "deliveryID"=>"53618", "testIDs"=>"", "deliveryIDs"=>"", "selectedRows"=>"2", "event"=>"", "section"=>"", "event=manage&amp;section=exam"=>"Manage selected exam"}

Parameters:

Returns:

  • (Hash)

    key=>value parameter pairs



1175
1176
1177
1178
1179
1180
1181
# File 'lib/arachni/element/form.rb', line 1175

def self.parse_request_body( body )
    body.to_s.split( '&' ).inject( {} ) do |h, pair|
        name, value = pair.split( '=', 2 )
        h[decode( name.to_s )] = decode( value )
        h
    end
end

Instance Method Details

#audit_id(injection_str = '', opts = {}) ⇒ Object



356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/arachni/element/form.rb', line 356

def audit_id( injection_str = '', opts = {} )
    str = if original?
              opts[:no_auditor] = true
              ORIGINAL_VALUES
          elsif sample?
              opts[:no_auditor] = true
              SAMPLE_VALUES
          else
              injection_str
          end

    super( str, opts )
end

#decode(str) ⇒ Object

See Also:



1222
1223
1224
# File 'lib/arachni/element/form.rb', line 1222

def decode( str )
    self.class.decode( str )
end

#dupObject



1226
1227
1228
1229
1230
# File 'lib/arachni/element/form.rb', line 1226

def dup
    f = super
    f.nonce_name = nonce_name.dup if nonce_name
    f
end

#encode(str) ⇒ Object

See Also:



1203
1204
1205
# File 'lib/arachni/element/form.rb', line 1203

def encode( str )
    self.class.encode( str )
end

#field_type_for(name) ⇒ String

Retrieves a field type for the given field name.

Examples:

html_form = "<form>\n    <input type='text' name='text-input' />\n    <input type='password' name='passwd' />\n    <input type='hidden' name='cant-see-this' />\n</form>\n"

f = Form.from_document( 'http://stuff.com', html_form ).first

p f.field_type_for 'text-input'
#=> "text"

p f.field_type_for 'passwd'
#=> "password"

p f.field_type_for 'cant-see-this'
#=> "hidden"

Parameters:

  • name (String)

    field name

Returns:



988
989
990
991
992
993
994
995
# File 'lib/arachni/element/form.rb', line 988

def field_type_for( name )
    return if !@raw['auditable']

    field = @raw['auditable'].select { |f| f['name'] == name }.first
    return if !field

    field['type'].to_s.downcase
end

#has_nonce?Bool

Returns true if the form contains a nonce, false otherwise.

Examples:

f = Form.new( 'http://stuff.com', { nonce_input: '' } )
p f.has_nonce?
#=> false

f.nonce_name = 'nonce_input'
p f.has_nonce?
#=> true

Returns:

  • (Bool)

    true if the form contains a nonce, false otherwise.



937
938
939
# File 'lib/arachni/element/form.rb', line 937

def has_nonce?
    !!nonce_name
end

#idString

Returns unique form ID.

Examples:

p f = Form.new( 'http://stuff.com', inputs: { name: 'value' } )
#=> #<Arachni::Element::Form:0x00000002190f80 @raw={:inputs=>{:name=>"value"}}, @url="http://stuff.com/", @hash=-432844557667991308, @opts={}, @action="http://stuff.com/", @method="post", @auditable={"name"=>"value"}, @orig={"name"=>"value"}>

p f.action
#=> "http://stuff.com/"

p f.method
#=> "post"

p f.auditable.keys
#=> ["name"]

p f.id
#=> "http://stuff.com/::post::[\"name\"]"

Returns:



132
133
134
# File 'lib/arachni/element/form.rb', line 132

def id
    id_from :auditable
end

#id_from(type = :auditable) ⇒ Object

Examples:

ap f = Form.new( 'http://stuff.com', inputs: { name: 'value' } )
#=> #<Arachni::Element::Form:0x01da8d78
#     attr_accessor :action = "http://stuff.com/",
#     attr_accessor :auditable = {
#         "name" => "value"
#     },
#     attr_accessor :method = "get",
#     attr_accessor :url = "http://stuff.com/",
#     attr_reader :hash = -277024459210456651,
#     attr_reader :opts = {},
#     attr_reader :orig = {
#         "name" => "value"
#     },
#     attr_reader :raw = {
#         :inputs => {
#             :name => "value"
#         }
#     }
# >

p f.action
#=> "http://stuff.com/"

p f.method
#=> "post"

p f.auditable.keys
#=> ["name"]

p f.id_from :auditable
#=> "http://stuff.com/::post::[\"name\"]"

p f.id
#=> "http://stuff.com/::post::[\"name\"]"

p f.id_from :original
#=> "http://stuff.com/::post::[\"name\"]"

f['new-input'] = 'new value'

p f.id_from :auditable
#=> "http://stuff.com/::post::[\"name\", \"new-input\"]"

p f.id
#=> "http://stuff.com/::post::[\"name\", \"new-input\"]"

p f.id_from :original
#=> "http://stuff.com/::post::[\"name\"]"


187
188
189
190
191
# File 'lib/arachni/element/form.rb', line 187

def id_from( type = :auditable )
    query_vars = parse_url_vars( self.action )
    "#{self.action.split( '?' ).first.to_s.split( ';' ).first}::" <<
        "#{self.method}::#{query_vars.merge( self.send( type ) ).keys.compact.sort.to_s}"
end

#mutations(seed, opts = {}) ⇒ Array<Form>

Overrides Mutable#mutations adding support for mutations with:

  • sample values (filled by Module::KeyFiller.fill)

  • original values

  • password fields requiring identical values (in order to pass server-side validation)

Examples:

Default

ap Form.new( 'http://stuff.com', { name: '' } ).mutations( 'seed' )
#=> [
#    [0] #<Arachni::Element::Form:0x017a74b0
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "seed"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = -1192640691543074696,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [1] #<Arachni::Element::Form:0x0157e8c8
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "arachni_nameseed"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 1303250124082341093,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [2] #<Arachni::Element::Form:0x0157ce38
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "seed\x00"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 1320080946243198326,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [3] #<Arachni::Element::Form:0x0157aa98
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "arachni_nameseed\x00"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 460190056788056230,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [4] #<Arachni::Element::Form:0x01570890
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "__original_values__",
#        attr_accessor :auditable = {
#            "name" => ""
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 1705259843882941132,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [5] #<Arachni::Element::Form:0x0156de38
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "__sample_values__",
#        attr_accessor :auditable = {
#            "name" => "arachni_name"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = -2130848815716189861,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >
#]

skip_orig: true

ap Form.new( 'http://stuff.com', { name: '' } ).mutations( 'seed', skip_orig: true )
#=> [
#    [0] #<Arachni::Element::Form:0x01b7ff10
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "seed"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 629695739693886457,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [1] #<Arachni::Element::Form:0x01b42f20
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "arachni_nameseed"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = -232906949296507781,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [2] #<Arachni::Element::Form:0x01b412d8
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "seed\x00"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = -2864669958217534791,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >,
#    [3] #<Arachni::Element::Form:0x01b466e8
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "name",
#        attr_accessor :auditable = {
#            "name" => "arachni_nameseed\x00"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 1368563420578923320,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#            "name" => ""
#        },
#        attr_reader :raw = {
#            :name => ""
#        }
#    >
#]

With mirrored password fields


html_form = "<form>\n    <input type='password' name='pasword' />\n    <input type='password' name='password-verify'/>\n</form>\n"

ap Form.from_document( 'http://stuff.com', html_form ).first.mutations( 'seed' )
#=> [
#    [0] #<Arachni::Element::Form:0x03193298
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "pasword",
#        attr_accessor :auditable = {
#                    "pasword" => "5543!%arachni_secret",
#            "password-verify" => "5543!%arachni_secret"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 2997273381350449172,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_reader :raw = {
#                "attrs" => {
#                "action" => "http://stuff.com/",
#                "method" => "get"
#            },
#             "textarea" => [],
#                "input" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ],
#               "select" => [],
#            "auditable" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ]
#        }
#    >,
#    [1] #<Arachni::Element::Form:0x0314b628
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "password-verify",
#        attr_accessor :auditable = {
#                    "pasword" => "seed",
#            "password-verify" => "seed"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 173670487606368134,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_reader :raw = {
#                "attrs" => {
#                "action" => "http://stuff.com/",
#                "method" => "get"
#            },
#             "textarea" => [],
#                "input" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ],
#               "select" => [],
#            "auditable" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ]
#        }
#    >,
#    [2] #<Arachni::Element::Form:0x0314a3e0
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "password-verify",
#        attr_accessor :auditable = {
#                    "pasword" => "5543!%arachni_secretseed",
#            "password-verify" => "5543!%arachni_secretseed"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 1194840267632333783,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_reader :raw = {
#                "attrs" => {
#                "action" => "http://stuff.com/",
#                "method" => "get"
#            },
#             "textarea" => [],
#                "input" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ],
#               "select" => [],
#            "auditable" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ]
#        }
#    >,
#    [3] #<Arachni::Element::Form:0x0314f228
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "password-verify",
#        attr_accessor :auditable = {
#                    "pasword" => "seed\x00",
#            "password-verify" => "seed\x00"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 1541287776305441593,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_reader :raw = {
#                "attrs" => {
#                "action" => "http://stuff.com/",
#                "method" => "get"
#            },
#             "textarea" => [],
#                "input" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ],
#               "select" => [],
#            "auditable" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ]
#        }
#    >,
#    [4] #<Arachni::Element::Form:0x0314e058
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "password-verify",
#        attr_accessor :auditable = {
#                    "pasword" => "5543!%arachni_secretseed\x00",
#            "password-verify" => "5543!%arachni_secretseed\x00"
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = -3700401397051376057,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_reader :raw = {
#                "attrs" => {
#                "action" => "http://stuff.com/",
#                "method" => "get"
#            },
#             "textarea" => [],
#                "input" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ],
#               "select" => [],
#            "auditable" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ]
#        }
#    >,
#    [5] #<Arachni::Element::Form:0x03154f20
#        attr_accessor :action = "http://stuff.com/",
#        attr_accessor :altered = "__original_values__",
#        attr_accessor :auditable = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_accessor :auditor = nil,
#        attr_accessor :method = "get",
#        attr_accessor :url = "http://stuff.com/",
#        attr_reader :hash = 4290791575672400429,
#        attr_reader :opts = {},
#        attr_reader :orig = {
#                    "pasword" => "",
#            "password-verify" => ""
#        },
#        attr_reader :raw = {
#                "attrs" => {
#                "action" => "http://stuff.com/",
#                "method" => "get"
#            },
#             "textarea" => [],
#                "input" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ],
#               "select" => [],
#            "auditable" => [
#                [0] {
#                    "type" => "password",
#                    "name" => "pasword"
#                },
#                [1] {
#                    "type" => "password",
#                    "name" => "password-verify"
#                }
#            ]
#        }
#    >
#]

Parameters:

  • seed (String)

    seed to inject

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

    mutation options

Options Hash (opts):

  • :skip_orig (Bool)

    Whether or not to skip adding a mutation holding original values and sample values

Returns:

See Also:



861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
# File 'lib/arachni/element/form.rb', line 861

def mutations( seed, opts = {} )
    opts = MUTATION_OPTIONS.merge( opts )
    var_combo = super( seed, opts )

    if !opts[:skip_orig]
        # this is the original hash, in case the default values
        # are valid and present us with new attack vectors
        elem = self.dup
        elem.altered = ORIGINAL_VALUES
        var_combo << elem

        elem = self.dup
        elem.auditable = Arachni::Module::KeyFiller.fill( auditable.dup )
        elem.altered = SAMPLE_VALUES
        var_combo << elem
    end

    # if there are two password type fields in the form there's a good
    # chance that it's a 'please retype your password' thing so make sure
    # that we have a variation which has identical password values
    password_fields = auditable.keys.
        select { |input| field_type_for( input ) == 'password' }

    # mirror the password fields
    if password_fields.size == 2
        var_combo.each do |f|
            f[password_fields[0]] = f[password_fields[1]]
        end.compact
    end

    var_combo.uniq
end

#nameString?

Returns name of the form if it has one.

Examples:

p f = Form.from_document( 'http://stuff.com', '<form name="stuff"></form>' ).first
#=> #<Arachni::Element::Form:0x00000001ddfa08 @raw={"attrs"=>{"name"=>"stuff", "action"=>"http://stuff.com/", "method"=>"get"}, "textarea"=>[], "input"=>[], "select"=>[], "auditable"=>[]}, @url="http://stuff.com/", @hash=1935432807676141374, @opts={}, @action="http://stuff.com/", @method="get", @auditable={}, @orig={}>

p f.name
#=> "stuff"

p f = Form.new( 'http://stuff.com', 'attrs' => { 'name' => 'john' } )
#=> #<Arachni::Element::Form:0x00000002b46160 @raw={"attrs"=>{"name"=>"john"}}, @url="http://stuff.com/", @hash=2710248079644781147, @opts={}, @action="http://stuff.com/", @method=nil, @auditable={}, @orig={}>

p f.name
#=> "john"

p Form.new( 'http://stuff.com' ).name
#=> nil

Returns:

  • (String, nil)

    name of the form if it has one



109
110
111
# File 'lib/arachni/element/form.rb', line 109

def name
    @raw['attrs']['name'] if @raw['attrs'].is_a?( Hash )
end

#original?Bool

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

Examples:

mutations = Form.new( 'http://stuff.com', inputs: { name: 'value' } ).mutations( 'seed' )

# mutations generally have seeds injected into their auditable inputs
ap mutations.first
#=> <Arachni::Element::Form:0x0327fdf0
#    attr_accessor :action = "http://stuff.com/",
#    attr_accessor :altered = "name",
#    attr_accessor :auditable = {
#        "name" => "seed"
#    },
#    attr_accessor :auditor = nil,
#    attr_accessor :method = "get",
#    attr_accessor :url = "http://stuff.com/",
#    attr_reader :hash = -3646163768215054761,
#    attr_reader :opts = {},
#    attr_reader :orig = {
#        "name" => "value"
#    },
#    attr_reader :raw = {
#        :inputs => {
#            :name => "value"
#        }
#    }
#>

p mutations.first.original?
#=> false

# but forms need to also be submitted with their default values
# for training purposes
ap original = mutations.select { |m| m.altered == Form::ORIGINAL_VALUES }.first
#=> #<Arachni::Element::Form:0x022a5a60
#     attr_accessor :action = "http://stuff.com/",
#     attr_accessor :altered = "__original_values__",
#     attr_accessor :auditable = {
#         "name" => "value"
#     },
#     attr_accessor :auditor = nil,
#     attr_accessor :method = "get",
#     attr_accessor :url = "http://stuff.com/",
#     attr_reader :hash = -608155834642701428,
#     attr_reader :opts = {},
#     attr_reader :orig = {
#         "name" => "value"
#     },
#     attr_reader :raw = {
#         :inputs => {
#             :name => "value"
#         }
#     }
# >

p original.original?
#=> true

Returns:

  • (Bool)

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



289
290
291
# File 'lib/arachni/element/form.rb', line 289

def original?
    self.altered == ORIGINAL_VALUES
end

#parse_request_body(body) ⇒ Object

See Also:



1183
1184
1185
# File 'lib/arachni/element/form.rb', line 1183

def parse_request_body( body )
    self.class.parse_request_body( body )
end

#requires_password?Bool

Checks whether or not the form contains 1 or more password fields.

Examples:

With password

html_form = "   <form>\n        <input type='password' name='pasword' />\n   </form>\n"

p Form.from_document( 'http://stuff.com', html_form ).first.requires_password?
#=> true

Without password

html_form = "   <form>\n        <input type='text' name='stuff' />\n   </form>\n"

p Form.from_document( 'http://stuff.com', html_form ).first.requires_password?
#=> false

Returns:

  • (Bool)

    true if the form contains passwords fields, false otherwise.



920
921
922
923
# File 'lib/arachni/element/form.rb', line 920

def requires_password?
    return if !self.raw.is_a?( Hash ) || !self.raw['input'].is_a?( Array )
    self.raw['input'].select { |i| i['type'] == 'password' }.any?
end

#sample?Bool

Returns true if the element has been populated with sample (Module::KeyFiller) values, false otherwise.

Examples:

mutations = Form.new( 'http://stuff.com', inputs: { name: '' } ).mutations( 'seed' )

# mutations generally have seeds injected into their auditable inputs
ap mutations.first
#=> <Arachni::Element::Form:0x0327fdf0
#    attr_accessor :action = "http://stuff.com/",
#    attr_accessor :altered = "name",
#    attr_accessor :auditable = {
#        "name" => "seed"
#    },
#    attr_accessor :auditor = nil,
#    attr_accessor :method = "get",
#    attr_accessor :url = "http://stuff.com/",
#    attr_reader :hash = -3646163768215054761,
#    attr_reader :opts = {},
#    attr_reader :orig = {
#        "name" => "value"
#    },
#    attr_reader :raw = {
#        :inputs => {
#            :name => "value"
#        }
#    }
#>

# when values are missing the inputs are filled in using sample values
ap sample = mutations.select { |m| m.altered == Form::SAMPLE_VALUES }.first
#=> #<Arachni::Element::Form:0x02b23020
#     attr_accessor :action = "http://stuff.com/",
#     attr_accessor :altered = "__sample_values__",
#     attr_accessor :auditable = {
#         "name" => "arachni_name"
#     },
#     attr_accessor :auditor = nil,
#     attr_accessor :method = "get",
#     attr_accessor :url = "http://stuff.com/",
#     attr_reader :hash = 205637814585882034,
#     attr_reader :opts = {},
#     attr_reader :orig = {
#         "name" => ""
#     },
#     attr_reader :raw = {
#         :inputs => {
#             :name => ""
#         }
#     }
# >

p sample.sample?
#=> true

Returns:

  • (Bool)

    true if the element has been populated with sample (Module::KeyFiller) values, false otherwise.

See Also:



352
353
354
# File 'lib/arachni/element/form.rb', line 352

def sample?
    self.altered == SAMPLE_VALUES
end

#simpleHash

Returns a simple representation of self including attributes and auditables.

Examples:

p Form.new( 'http://stuff.com', inputs: { name: 'value' } ).simple
#=> {"auditable"=>{"name"=>"value"}, "attrs"=>{"method"=>"post", "action"=>"http://stuff.com/"}}

p Form.new( 'http://stuff.com', method: 'post', inputs: { name: 'value' } ).simple
#=> {"auditable"=>{"name"=>"value"}, "attrs"=>{"method"=>'post', "action"=>"http://stuff.com/"}}

Returns:

  • (Hash)

    a simple representation of self including attributes and auditables



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
# File 'lib/arachni/element/form.rb', line 203

def simple
    form = {}

    form['auditable'] = {}
    if @raw['auditable'] && !@raw['auditable'].empty?
        @raw['auditable'].each do |item|
            next if !item['name']
            form['auditable'][item['name']] = item['value']
        end
    end

    if @raw['attrs']
        form['attrs'] = @raw['attrs']
    else
        form['attrs'] = {
            'method' => @method,
            'action' => @action
        }
    end

    if form['auditable'].empty? && @auditable && !@auditable.empty?
        form['auditable'] = @auditable
    end

    form.dup
end

#typeString

Returns ‘form’.

Returns:



998
999
1000
# File 'lib/arachni/element/form.rb', line 998

def type
    Arachni::Element::FORM
end