module Arachni::Element::Capabilities::Analyzable::Signature
Looks for specific substrings or patterns in response bodies.
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Constants
- FILE_SIGNATURES
- FILE_SIGNATURES_PER_PLATFORM
- LINE_BUFFER_SIZE
- SIGNATURE_CACHE
- SIGNATURE_OPTIONS
- SOURCE_CODE_SIGNATURES_PER_PLATFORM
Public Instance Methods
get_matches( response )
click to toggle source
Tries to identify an issue through pattern matching.
If a issue is found a message will be printed and the issue will be logged.
@param [HTTP::Response] response
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 145 def get_matches( response ) vector = response.request.performer opts = vector.audit_options.dup if !opts[:signatures].is_a?( Array ) && !opts[:signatures].is_a?( Hash ) opts[:signatures] = [opts[:signatures]] end opts[:signatures] = [vector.seed] if opts[:signatures].empty? find_signatures( opts[:signatures], response, opts.dup ) end
signature_analysis( payloads, opts = { } )
click to toggle source
Performs signatures analysis and logs an issue, should there be one.
It logs an issue when:
-
‘:match` == nil AND `:regexp` matches the response body
-
‘:match` != nil AND `:regexp` match == `:match`
-
‘:substring` exists in the response body
@param [String, Array
<String>, Hash{Symbol => <String, Array
<String>>}] payloads
Payloads to inject, if given: * {String} -- Will inject the single payload. * {Array} -- Will iterate over all payloads and inject them. * {Hash} -- Expects {Platform} (as `Symbol`s ) for keys and {Array} of `payloads` for values. The applicable `payloads` will be {Platform::Manager#pick picked} from the hash based on {Element::Capabilities::Submittable#platforms applicable platforms} for the {Element::Capabilities::Submittable#action resource} to be audited.
@param [Hash] opts
Options as described in {Arachni::Element::Auditable::OPTIONS} and {SIGNATURE_OPTIONS}.
@return [Bool]
`true` if the audit was scheduled successfully, `false` otherwise (like if the resource is out of scope).
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 118 def signature_analysis( payloads, opts = { } ) return false if self.inputs.empty? if scope.out? print_debug 'Signature analysis: Element is out of scope,' << " skipping: #{audit_id}" return false end # Buffer possible issues, we'll only register them with the system once # we've evaluated our control response. @candidate_issues = [] opts = self.class::OPTIONS.merge( SIGNATURE_OPTIONS.merge( opts ) ) fail_if_signatures_invalid( opts[:signatures] ) audit( payloads, opts ) { |response| get_matches( response ) } end
Private Instance Methods
control_and_log( issue )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 304 def control_and_log( issue ) control = issue[:vector].dup if control.parameter_name_audit? inputs = control.inputs.dup value = inputs.delete( control.seed ) control.inputs = inputs.merge( Utilities.random_seed => value ) else control.affected_input_value = Utilities.random_seed end control.submit do |response| # Something has gone wrong, timed-out request or closed connection. # If we can't verify the issue bail out... next if response.code == 0 # If the signature matches the control response don't bother, it'll # be a coincidence causing a false positive. next if signature_match?( issue[:signature], response ) # We can't have procs in there, we only log stuff that # can be serialized. issue[:vector].audit_options.delete :signatures @auditor.log( issue ) end end
fail_if_signatures_invalid( signatures )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 159 def fail_if_signatures_invalid( signatures ) case signatures when Regexp if (signatures.options & Regexp::MULTILINE) == Regexp::MULTILINE fail ArgumentError, 'Multi-line regular expressions are not supported.' end when Array signatures.each { |s| fail_if_signatures_invalid s } when Hash fail_if_signatures_invalid signatures.values end end
find_signature( signature, response, opts )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 224 def find_signature( signature, response, opts ) if signature.respond_to?( :call ) signature = signature.call( response ) end return if !signature if signature.is_a? Regexp match_regexp_and_log( signature, response, opts ) else find_substring_and_log( signature, response, opts ) end end
find_signatures( signatures, response, opts )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 175 def find_signatures( signatures, response, opts ) k = [signatures, response.body] return if SIGNATURE_CACHE[:match][k] == false case signatures when Regexp, String, Array [signatures].flatten.compact.each do |signature| res = find_signature( signature, response, opts ) SIGNATURE_CACHE[:match][k] ||= !!res end when Hash if opts[:platform] && signatures[opts[:platform]] [signatures[opts[:platform]]].flatten.compact.each do |p| [p].flatten.compact.each do |signature| res = find_signature( signature, response, opts ) SIGNATURE_CACHE[:match][k] ||= !!res end end else signatures.each do |platform, p| dopts = opts.dup dopts[:platform] = platform [p].flatten.compact.each do |signature| res = find_signature( signature, response, dopts ) SIGNATURE_CACHE[:match][k] ||= !!res end end end return if !opts[:payload_platforms] # Find out if there are any signatures without associated payloads # and match them against every payload's response. signatures.select { |p, _| !opts[:payload_platforms].include?( p ) }. each do |platform, p| dopts = opts.dup dopts[:platform] = platform [p].flatten.compact.each do |signature| res = find_signature( signature, response, dopts ) SIGNATURE_CACHE[:match][k] ||= !!res end end end end
find_substring_and_log( substring, response, opts )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 238 def find_substring_and_log( substring, response, opts ) return if substring.to_s.empty? k = [substring, response.body] return if SIGNATURE_CACHE[:match][k] == false SIGNATURE_CACHE[:match][k] = includes = response.body.include?( substring ) return if !includes || ignore?( response, opts ) control_and_log( response: response, platform: opts[:platform], proof: substring, signature: substring, vector: response.request.performer ) true end
ignore?( response, opts )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 282 def ignore?( response, opts ) [opts[:ignore]].flatten.compact.each do |signature| return true if signature_match?( signature, response ) end false end
match_regexp_and_log( regexp, response, opts )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 258 def match_regexp_and_log( regexp, response, opts ) k = [regexp, response.body] return if SIGNATURE_CACHE[:match][k] == false match_data = response.body.match( regexp ) return if !match_data match_data = match_data[0].to_s SIGNATURE_CACHE[:match][k] = !match_data.empty? return if match_data.empty? || ignore?( response, opts ) control_and_log( response: response, platform: opts[:platform], proof: match_data, signature: regexp, vector: response.request.performer ) true end
signature_match?( signature, response )
click to toggle source
# File lib/arachni/element/capabilities/analyzable/signature.rb, line 290 def signature_match?( signature, response ) if signature.respond_to?( :call ) signature = signature.call( response ) end return if !signature if signature.is_a? Regexp response.body =~ signature else response.body.include?( signature ) end end