Class: Arachni::Element::NestedCookie

Inherits:
Base show all
Includes:
Capabilities::Analyzable, Capabilities::Auditable, Capabilities::Auditable::Buffered, Capabilities::Auditable::LineBuffered, Capabilities::Inputtable, Capabilities::Mutable, Capabilities::Submittable, Capabilities::WithSource
Defined in:
lib/arachni/element/nested_cookie.rb,
lib/arachni/element/nested_cookie/capabilities/submittable.rb

Overview

Author:

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

Defined Under Namespace

Modules: Capabilities

Constant Summary collapse

DEFAULT =

Default cookie values

Cookie::DEFAULT

Constants included from Capabilities::Analyzable::Differential

Capabilities::Analyzable::Differential::DIFFERENTIAL_ALLOWED_STATUS, Capabilities::Analyzable::Differential::DIFFERENTIAL_OPTIONS

Constants included from Capabilities::Analyzable::Timeout

Capabilities::Analyzable::Timeout::TIMEOUT_OPTIONS

Constants included from Capabilities::Analyzable::Signature

Capabilities::Analyzable::Signature::FILE_SIGNATURES, Capabilities::Analyzable::Signature::FILE_SIGNATURES_PER_PLATFORM, Capabilities::Analyzable::Signature::LINE_BUFFER_SIZE, Capabilities::Analyzable::Signature::SIGNATURE_CACHE, Capabilities::Analyzable::Signature::SIGNATURE_OPTIONS, Capabilities::Analyzable::Signature::SOURCE_CODE_SIGNATURES_PER_PLATFORM

Constants included from Capabilities::Auditable::LineBuffered

Capabilities::Auditable::LineBuffered::DEFAULT_LINE_BUFFER_SIZE

Constants included from Capabilities::Auditable::Buffered

Capabilities::Auditable::Buffered::DEFAULT_BUFFER_SIZE

Constants included from Capabilities::Auditable

Capabilities::Auditable::OPTIONS

Constants included from Capabilities::Mutable

Capabilities::Mutable::EXTRA_NAME, Capabilities::Mutable::FUZZ_NAME, Capabilities::Mutable::FUZZ_NAME_VALUE, Capabilities::Mutable::MUTATION_OPTIONS

Constants included from Capabilities::Inputtable

Capabilities::Inputtable::INPUTTABLE_CACHE

Constants inherited from Base

Base::MAX_SIZE

Instance Attribute Summary collapse

Attributes included from Capabilities::WithSource

#source

Attributes included from Capabilities::Analyzable::Differential

#differential_analysis_options

Attributes included from Capabilities::Analyzable::Timeout

#timing_attack_remark_data

Attributes included from Capabilities::Auditable

#audit_options

Attributes included from Capabilities::WithAuditor

#auditor

Attributes included from Capabilities::Mutable

#affected_input_name, #format, #seed

Attributes included from Capabilities::Inputtable

#default_inputs, #inputs, #raw_inputs

Attributes inherited from Base

#initialization_options, #page

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Capabilities::Submittable

#action, #action=, #dup, #http, #id, #method, #method=, #platforms, #submit, #to_h

Methods included from Capabilities::WithSource

#dup, #to_h

Methods included from Capabilities::Analyzable

has_timeout_candidates?, reset, timeout_audit_run

Methods included from Capabilities::Analyzable::Differential

#differential_analysis, #dup, reset

Methods included from Capabilities::Analyzable::Timeout

add_phase_2_candidate, candidates_include?, deduplicate, deduplicate?, do_not_deduplicate, #dup, #ensure_responsiveness, has_candidates?, payload_delay_from_options, reset, run, #timeout_analysis, timeout_from_options, #timeout_id, #timing_attack_probe, #timing_attack_verify

Methods included from Capabilities::Analyzable::Signature

#get_matches, #signature_analysis

Methods included from Capabilities::Auditable::LineBuffered

#line_buffered_audit

Methods included from Capabilities::Auditable::Buffered

#buffered_audit

Methods included from Capabilities::Auditable

#audit, #audit_id, #audit_status_message, #audit_status_message_action, #audit_verbose_message, #coverage_hash, #coverage_id, #dup, #matches_skip_like_blocks?, reset, #reset, #skip?, skip_like

Methods included from Capabilities::WithAuditor

#dup, #marshal_dump, #orphan?, #prepare_for_report, #remove_auditor

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 Capabilities::Mutable

#affected_input_value, #affected_input_value=, #dup, #each_mutation, #immutables, #inspect, #mutation?, #mutations, #parameter_name_audit?, #reset, #switch_method, #to_h, #with_raw_payload, #with_raw_payload?

Methods included from Capabilities::Inputtable

#[], #[]=, #changes, #dup, #has_inputs?, #inputtable_id, inputtable_id, #raw_input?, #reset, #to_h, #try_input, #update, #updated?, #valid_input_data?, #valid_input_name?, #valid_input_name_data?, #valid_input_value?, #valid_input_value_data?

Methods inherited from Base

#==, #action, #dup, #hash, #id, #marshal_dump, #marshal_load, #persistent_hash, #prepare_for_report, #reset, #to_h, #to_hash, too_big?, #type, #url, #url=

Methods included from Capabilities::WithScope

#scope

Constructor Details

#initialize(options) ⇒ NestedCookie

Returns a new instance of NestedCookie.

Parameters:

  • options (Hash)

    For options see DEFAULT, with the following extras:

Options Hash (options):

  • :url (String)

    URL of the page which created the cookie – *required*.

  • :action (String)

    URL of the page to submit the cookie – defaults to `:url`.

  • :inputs (Hash)

    Allows you to pass cookie data as a `name => value` pair instead of the more complex DEFAULT structure.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/arachni/element/nested_cookie.rb', line 44

def initialize( options )
    @data = {}
    super( options )

    if options[:name] && options[:value]
        options[:name]  = options[:name].to_s.recode
        options[:value] = options[:value].to_s.recode

        self.inputs = self.class.parse_inputs( options[:value] )
        @data.merge!( options )
    else
        self.inputs = (options[:inputs] || {}).dup
    end

    @data.merge!( DEFAULT.merge( @data ) )
    @data[:value] = decode( @data[:value].to_s ) rescue @data[:value].to_s

    parsed_uri = uri_parse( action )
    if !@data[:path]
        path = parsed_uri.path
        path = !path.empty? ? path : '/'
        @data[:path] = path
    end

    if @data[:expires] && !@data[:expires].is_a?( Time )
        @data[:expires] = Time.parse( @data[:expires].to_s ) rescue nil
    end

    @data[:domain] ||= parsed_uri.host

    @default_inputs = self.inputs.dup.freeze
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args, &block) ⇒ Object

Uses the method name as a key to cookie attributes in DEFAULT.



125
126
127
128
# File 'lib/arachni/element/nested_cookie.rb', line 125

def method_missing( sym, *args, &block )
    return @data[sym] if @data.include? sym
    super( sym, *args, &block )
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



33
34
35
# File 'lib/arachni/element/nested_cookie.rb', line 33

def data
  @data
end

Class Method Details

.decode(str) ⇒ String

Decodes a String encoded for the `Cookie` header field.

Parameters:

Returns:



351
352
353
# File 'lib/arachni/element/nested_cookie.rb', line 351

def decode( str )
    Cookie.decode( str )
end

.encode(str) ⇒ String

Encodes a String's reserved characters in order to prepare it for the `Cookie` header field.

Parameters:

Returns:



342
343
344
# File 'lib/arachni/element/nested_cookie.rb', line 342

def encode( str )
    Cookie.encode( str )
end

.from_cookies(cookies) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/arachni/element/nested_cookie.rb', line 311

def from_cookies( cookies )
    [cookies].flatten.compact.map do |cookie|
        next if !cookie.value.include?( '=' )

        inputs = parse_inputs( cookie.value )
        next if inputs.empty?

        new({
            url:    cookie.url,
            action: cookie.action,
            method: cookie.method,
            inputs: inputs,
            source: cookie.source
        }.merge( cookie.data ))
    end.compact
end

.from_file(url, filepath) ⇒ Array<NestedCookie>

Parses a Netscape Cookie-jar into an Array of Cookie.

Parameters:

Returns:

See Also:



239
240
241
# File 'lib/arachni/element/nested_cookie.rb', line 239

def from_file( url, filepath )
    from_cookies( Cookie.from_file( url, filepath ) )
end

.from_headers(url, headers) ⇒ Array<NestedCookie>

Extracts cookies from the `Set-Cookie` HTTP response header field.

Parameters:

Returns:

See Also:

  • forms_set_cookie


281
282
283
# File 'lib/arachni/element/nested_cookie.rb', line 281

def from_headers( url, headers )
    from_cookies( Cookie.from_headers( url, headers ) )
end

.from_parser(parser) ⇒ Array<NestedCookie>

Extracts cookies from a document based on `Set-Cookie` `http-equiv` meta tags.

Parameters:

Returns:

See Also:



264
265
266
# File 'lib/arachni/element/nested_cookie.rb', line 264

def from_parser( parser )
    from_cookies( Cookie.from_parser( parser ) )
end

.from_response(response) ⇒ Array<NestedCookie>

Extracts cookies from an HTTP response.

Parameters:

Returns:

See Also:



251
252
253
254
# File 'lib/arachni/element/nested_cookie.rb', line 251

def from_response( response )
    from_parser( Arachni::Parser.new( response ) ) +
        from_headers( response.url, response.headers )
end

.from_rpc_data(data) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/arachni/element/nested_cookie.rb', line 214

def from_rpc_data( data )
    if data['initialization_options']['expires']
        data['initialization_options']['expires'] =
            Time.parse( data['initialization_options']['expires'] )
    end

    if data['data']['expires']
        data['data']['expires'] = Time.parse( data['data']['expires'] )
    end

    data['data'] = data['data'].my_symbolize_keys(false)

    super data
end

Parses the `Set-Cookie` header value into cookie elements.

Parameters:

Returns:



293
294
295
# File 'lib/arachni/element/nested_cookie.rb', line 293

def from_set_cookie( url, str )
    from_cookies( Cookie.from_set_cookie( url, str ) )
end

.from_string(url, string) ⇒ Array<NestedCookie>

Parses a string formatted for the `Cookie` HTTP request header field into cookie elements.

Parameters:

Returns:



307
308
309
# File 'lib/arachni/element/nested_cookie.rb', line 307

def from_string( url, string )
    from_cookies( Cookie.from_string( url, string ) )
end

.in_html?(html) ⇒ Boolean

Returns:

  • (Boolean)


268
269
270
# File 'lib/arachni/element/nested_cookie.rb', line 268

def in_html?( html )
    html =~ /set-cookie.*&/i
end

.parse_inputs(value) ⇒ Object



328
329
330
331
332
333
334
# File 'lib/arachni/element/nested_cookie.rb', line 328

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

.typeObject



210
211
212
# File 'lib/arachni/element/nested_cookie.rb', line 210

def type
    :nested_cookie
end

Instance Method Details

#decode(str) ⇒ Object

See Also:



188
189
190
# File 'lib/arachni/element/nested_cookie.rb', line 188

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

#encode(*args) ⇒ Object

See Also:



183
184
185
# File 'lib/arachni/element/nested_cookie.rb', line 183

def encode( *args )
    self.class.encode( *args )
end

#expired?(time = Time.now) ⇒ Boolean

Indicates whether or not the cookie has expired.

Parameters:

  • time (Time) (defaults to: Time.now)

    To compare against.

Returns:

  • (Boolean)


113
114
115
# File 'lib/arachni/element/nested_cookie.rb', line 113

def expired?( time = Time.now )
    expires_at != nil && time > expires_at
end

#expires_atTime, NilClass

Returns Expiration `Time` of the cookie or `nil` if it doesn't have one (i.e. is a session cookie).

Returns:

  • (Time, NilClass)

    Expiration `Time` of the cookie or `nil` if it doesn't have one (i.e. is a session cookie).



103
104
105
# File 'lib/arachni/element/nested_cookie.rb', line 103

def expires_at
    @data[:expires]
end

#http_only?Bool

Indicates whether the cookie is safe from modification from client-side code.

Returns:

  • (Bool)


87
88
89
# File 'lib/arachni/element/nested_cookie.rb', line 87

def http_only?
    @data[:httponly] == true
end

#respond_to?(*args) ⇒ Bool

Used by #method_missing to determine if it should process the call.

Returns:

  • (Bool)


134
135
136
# File 'lib/arachni/element/nested_cookie.rb', line 134

def respond_to?( *args )
    (@data && @data.include?( args.first )) || super
end

#secure?Bool

Indicates whether the cookie must be only sent over an encrypted channel.

Returns:

  • (Bool)


80
81
82
# File 'lib/arachni/element/nested_cookie.rb', line 80

def secure?
    @data[:secure] == true
end

#session?Bool

Indicates whether the cookie is to be discarded at the end of the session.

Doesn't play a role during the scan but it can provide useful info to checks and such.

Returns:

  • (Bool)


96
97
98
# File 'lib/arachni/element/nested_cookie.rb', line 96

def session?
    @data[:expires].nil?
end

#simpleHash

Returns Simple representation of the cookie as a hash – with the cookie name as `key` and the cookie value as `value`.

Returns:

  • (Hash)

    Simple representation of the cookie as a hash – with the cookie name as `key` and the cookie value as `value`.



120
121
122
# File 'lib/arachni/element/nested_cookie.rb', line 120

def simple
    self.inputs.dup
end

#to_rpc_dataObject



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/arachni/element/nested_cookie.rb', line 192

def to_rpc_data
    h = super

    if h['initialization_options']['expires']
        h['initialization_options']['expires'] =
            h['initialization_options']['expires'].to_s
    end

    h['data'] = h['data'].my_stringify_keys(false)
    if h['data']['expires']
        h['data']['expires'] = h['data']['expires'].to_s
    end

    h
end

#to_sString

Returns To be used in a `Cookie` HTTP request header.

Returns:

  • (String)

    To be used in a `Cookie` HTTP request header.



144
145
146
147
148
149
150
151
152
153
# File 'lib/arachni/element/nested_cookie.rb', line 144

def to_s
    # Only do encoding if we're dealing with updated inputs, otherwise pass
    # along the raw data as set in order to deal with server-side decoding
    # quirks.
    if updated? || !(raw_name || raw_value )
        "#{encode( name )}=#{value}"
    else
        "#{raw_name}=#{raw_value}"
    end
end

Returns Converts self to a `Set-Cookie` string.

Returns:

  • (String)

    Converts self to a `Set-Cookie` string.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/arachni/element/nested_cookie.rb', line 157

def to_set_cookie
    set_cookie = "#{self.to_s}"

    @data.each do |k, v|
        next if !v || !Cookie.keep_for_set_cookie.include?( k )

        set_cookie << "; #{k.capitalize}=#{v}"
    end

    set_cookie << '; Secure'   if secure?
    set_cookie << '; HttpOnly' if http_only?

    # If we want to set a cookie for only the domain that responded to the
    # request, Set-Cookie should not specify a domain.
    #
    # If we want the cookie to apply to all subdomains, we need to either
    # specify a dot-prefixed domain or a domain, the browser client will
    # prefix the dot anyways.
    #
    # http://stackoverflow.com/questions/1062963/how-do-browser-cookie-domains-work/1063760#1063760
    set_cookie << "; Domain=#{domain}" if domain.start_with?( '.' )

    set_cookie
end

#valueObject



138
139
140
# File 'lib/arachni/element/nested_cookie.rb', line 138

def value
    self.inputs.map { |n, v| "#{encode( n )}=#{encode( v )}" }.join( '&' )
end