Module: Arachni::Element::Capabilities::Analyzable::Signature

Included in:
Arachni::Element::Capabilities::Analyzable
Defined in:
lib/arachni/element/capabilities/analyzable/signature.rb

Overview

Looks for specific substrings or patterns in response bodies.

Author:

Constant Summary collapse

SIGNATURE_CACHE =
{
    match: Support::Cache::LeastRecentlyPushed.new( 10_000 )
}
SIGNATURE_OPTIONS =
{
    # The signatures to look for in each line of the response body,
    # if `Regexp` it will be matched against it, if `String` it'll be used
    # as a needle.
    #
    # Multi-line Regexp is not supported.
    signatures: [],

    # Array of signatures to ignore.
    #
    # Useful when needing to narrow down what to log without having to
    # construct overly complex signatures.
    ignore:     []
}
FILE_SIGNATURES =
{
    'environ'  => proc do |response|
        next if !response.body.include?( 'DOCUMENT_ROOT=' )
        /DOCUMENT_ROOT=.*HTTP_USER_AGENT=/
    end,
    'passwd'   => proc do |response|
        if response.body.include?( 'bin/' )
            /:.+:\d+:\d+:.+:[0-9a-zA-Z\/]+/

        # Response may have encoded chars as HTML entities.
        elsif response.body.include?( 'bin/' ) && response.body.include?( ':' )
            /:.+:\d+:\d+:.+:[0-9a-zA-Z&#;]+/
        end
    end,
    'boot.ini' => '[boot loader]',
    'win.ini'  => '[extensions]',
    'web.xml'  => '<web-app'
}
FILE_SIGNATURES_PER_PLATFORM =
{
    unix:   [
        FILE_SIGNATURES['environ'],
        FILE_SIGNATURES['passwd']
    ],
    windows: [
        FILE_SIGNATURES['boot.ini'],
        FILE_SIGNATURES['win.ini']
    ],
    java:    [
        FILE_SIGNATURES['web.xml']
    ]
}
SOURCE_CODE_SIGNATURES_PER_PLATFORM =
{
    php:  [
        '<?php'
    ],

    # No need to optimize the following with procs, OR'ed Regexps perform
    # better than multiple substring checks, so long as the Regexp parts are
    # fairly simple.

    java: [
        /<%|<%=|<%@\s+page|<%@\s+include|<%--|import\s+javax.servlet|
            import\s+java.io|import=['"]java.io|request\.getParameterValues\(|
            response\.setHeader|response\.setIntHeader\(/
    ],
    asp:  [
        /<%|Response\.Write|Request\.Form|Request\.QueryString|
            Response\.Flush|Session\.SessionID|Session\.Timeout|
            Server\.CreateObject|Server\.MapPath/
    ]
}
LINE_BUFFER_SIZE =
1_000

Instance Method Summary collapse

Instance Method Details

#get_matches(response) ⇒ Object

Tries to identify an issue through pattern matching.

If a issue is found a message will be printed and the issue will be logged.

Parameters:


145
146
147
148
149
150
151
152
153
154
155
156
# 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 = { }) ⇒ Bool

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

Parameters:

Returns:

  • (Bool)

    true if the audit was scheduled successfully, false otherwise (like if the resource is out of scope).


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# 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