Class: Arachni::Checks::SourceCodeDisclosure

Inherits:
Arachni::Check::Base show all
Defined in:
components/checks/active/source_code_disclosure.rb

Overview

Identifies source code disclosures by injecting a known server-side file into all input vectors and then inspects the responses for the existence of source code.

See Also:

Author:

Constant Summary

Constants included from Arachni::Check::Auditor

Arachni::Check::Auditor::DOM_ELEMENTS_WITH_INPUTS, Arachni::Check::Auditor::ELEMENTS_WITH_INPUTS, Arachni::Check::Auditor::FILE_SIGNATURES, Arachni::Check::Auditor::FILE_SIGNATURES_PER_PLATFORM, Arachni::Check::Auditor::Format, Arachni::Check::Auditor::SOURCE_CODE_SIGNATURES_PER_PLATFORM

Constants included from Arachni

BANNER, Arachni::Cookie, Form, Header, JSON, Link, LinkTemplate, Severity, UIForm, UIInput, VERSION, WEBSITE, WIKI, XML

Instance Attribute Summary

Attributes included from Arachni::Check::Auditor

#framework, #page

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Arachni::Check::Base

#browser_cluster, #clean_up, elements, exempt_platforms, has_exempt_platforms?, has_platforms?, #initialize, platforms, #plugins, prefer, #preferred, preferred, #session, supports_platforms?

Methods included from Arachni::Check::Auditor

#audit, #audit_differential, #audit_signature, #audit_timeout, #audited, #audited?, #buffered_audit, #each_candidate_dom_element, #each_candidate_element, has_timeout_candidates?, #http, #initialize, #log, #log_issue, #log_remote_file, #log_remote_file_if_exists, #match_and_log, #max_issues, #preferred, reset, #skip?, timeout_audit_run, #trace_taint, #with_browser, #with_browser_cluster

Methods inherited from Arachni::Component::Base

author, description, fullname, #shortname, shortname, shortname=, version

Methods included from Arachni::Component::Output

#depersonalize_output, #depersonalize_output?, #intercept_print_message

Methods included from UI::Output

#caller_location, #debug?, #debug_level, #debug_level_1?, #debug_level_2?, #debug_level_3?, #debug_level_4?, #debug_off, #debug_on, #disable_only_positives, #error_buffer, #error_log_fd, #error_logfile, #has_error_log?, #included, #log_error, #mute, #muted?, #only_positives, #only_positives?, #print_bad, #print_debug, #print_debug_backtrace, #print_debug_exception, #print_debug_level_1, #print_debug_level_2, #print_debug_level_3, #print_debug_level_4, #print_error, #print_error_backtrace, #print_exception, #print_info, #print_line, #print_ok, #print_status, #print_verbose, #reroute_to_file, #reroute_to_file?, reset_output_options, #set_error_logfile, #unmute, #verbose?, #verbose_off, #verbose_on

Methods included from Arachni::Component::Utilities

#read_file

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 Arachni

URI, collect_young_objects, #get_long_win32_filename, jruby?, null_device, profile?, windows?

Constructor Details

This class inherits a constructor from Arachni::Check::Base

Class Method Details

.infoObject


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'components/checks/active/source_code_disclosure.rb', line 100

def self.info
    {
        name:        'Source code disclosure',
        description: %q{
It tries to identify whether or not the web application can be forced to reveal
source code.
},
        elements:    ELEMENTS_WITH_INPUTS,
        author:      'Tasos "Zapotek" Laskos <[email protected]>',
        version:     '0.2.4',
        platforms:   options[:signatures].keys,

        issue:       {
            name:            %q{Source code disclosure},
            description:     %q{
A modern web application will be reliant on several different programming languages.

These languages can be broken up in two flavours. These are client-side languages
(such as those that run in the browser -- like JavaScript) and server-side
languages (which are executed by the server -- like ASP, PHP, JSP, etc.) to form
the dynamic pages (client-side code) that are then sent to the client.

Because all server side code should be executed by the server, it should never be
seen by the client. However in some scenarios, it is possible that:

1. The server side code has syntax errors and therefore is not executed by the
server but is instead sent to the client.
2. Using crafted requests it is possible to force the server into displaying the
source code of the application without executing it.

As the server-side source code often contains sensitive information, such as
database connection strings or details into the application workflow, this can be
extremely risky.

Cyber-criminals will attempt to discover pages that either accidentally or
forcefully allow the server-side source code to be disclosed, to assist in
discovering further vulnerabilities or sensitive information.

Arachni has detected server-side source code within the server's response.

_(False positives may occur when requesting binary files such as images
(.JPG or .PNG) and may require manual verification.)_
},
            references:  {
                'CWE' => 'http://cwe.mitre.org/data/definitions/540.html'
            },
            tags:            %w(code source file inclusion disclosure),
            cwe:             540,
            severity:        Severity::HIGH,
            remedy_guidance: %q{
It is important that input sanitisation be conducted to prevent application files
(ASP, JSP, PHP or config files) from being called. It is also important that the
file system permissions are correctly configured and that all unused files are
removed from the web root.

If these are not an option, then the vulnerable file should be removed from the server.
}
        }
    }
end

.optionsObject


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'components/checks/active/source_code_disclosure.rb', line 18

def self.options
    @options ||= {
        format:     [Format::STRAIGHT],
        signatures: SOURCE_CODE_SIGNATURES_PER_PLATFORM,

        # Add one more mutation (on the fly) which will include the extension
        # of the original value (if that value was a filename) after a null byte.
        each_mutation: proc do |mutation|
            next if !mutation.affected_input_value

            # Don't bother if the current element type can't carry nulls.
            next if !mutation.valid_input_value_data?( "\0" )

            m = mutation.dup

            # Figure out the extension of the default value, if it has one.
            ext = m.default_inputs[m.affected_input_name].to_s.split( '.' )
            ext = ext.size > 1 ? ext.last : nil

            # If the extension of the default value is the same as of the
            # payload there's no need to add an extra mutation.
            next if ext == mutation.affected_input_value.split( '.' ).last

            # Null-terminate the injected value and append the ext.
            m.affected_input_value += "\0.#{ext}"

            # Pass our new element back to be audited.
            m
        end
    }
end

.payloadObject


54
55
56
# File 'components/checks/active/source_code_disclosure.rb', line 54

def self.payload
    @payload
end

.payload=(file) ⇒ Object


50
51
52
# File 'components/checks/active/source_code_disclosure.rb', line 50

def self.payload=( file )
    @payload = file
end

.payloadsObject


58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'components/checks/active/source_code_disclosure.rb', line 58

def self.payloads
    return [] if !payload

    parsed_url    = uri_parse( payload )
    directories   = parsed_url.path.split( '/' )
    resource_name = directories.pop

    directories.reject!{ |d| d.empty? }

    ["/#{resource_name}"] + directories.reverse.inject([]) do |plds, directory|
        plds << "#{directory}/#{plds.last}"
    end.map { |pld| "/#{pld}#{resource_name}" }
end

.supported_extensionsObject


72
73
74
75
# File 'components/checks/active/source_code_disclosure.rb', line 72

def self.supported_extensions
    @supported_extensions ||=
        Set.new([ 'jsp', 'asp', 'aspx', 'php', 'htm', 'html' ])
end

Instance Method Details

#prepareObject


77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'components/checks/active/source_code_disclosure.rb', line 77

def prepare
    candidate_paths  = page.paths
    candidate_paths |= page.jsons.map(&:action)
    candidate_paths |= page.xmls.map(&:action)

    # Let's look for fresh a payload -- i.e. an identifiable server-side resource.
    candidate_paths.each do |path|
        parsed_path = uri_parse( path )
        next if !self.class.supported_extensions.include?( parsed_path.resource_extension )

        self.class.payload = uri_parse( parsed_path.without_query ).path
        break
    end
end

#runObject


92
93
94
95
96
97
98
# File 'components/checks/active/source_code_disclosure.rb', line 92

def run
    return if self.class.payloads.empty?

    each_candidate_element do |element|
        element.signature_analysis( self.class.payloads, self.class.options )
    end
end