Class: Arachni::Checks::FileInclusion

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

Overview

File inclusion check.

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, NestedCookie, 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, #prepare, #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


87
88
89
90
91
92
93
94
95
96
97
98
99
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
# File 'components/checks/active/file_inclusion.rb', line 87

def self.info
    {
        name:        'File Inclusion',
        description: %q{
Injects paths of common files (like `/etc/passwd` and `boot.ini`) and evaluates
the existence of a file inclusion vulnerability based on the presence of relevant
content or errors in the HTTP response body.
},
        elements:    ELEMENTS_WITH_INPUTS,
        author:      'Tasos "Zapotek" Laskos <[email protected]> ',
        version:     '0.1.7',
        platforms:   options[:signatures].keys,

        issue:       {
            name:            %q{File Inclusion},
            description:     %q{
Web applications occasionally use parameter values to store the location of a file
which will later be required by the server.

An example of this is often seen in error pages, where the actual file path for
the error page is stored in a parameter value -- for example `example.com/error.php?page=404.php`.

A file inclusion occurs when the parameter value (ie. path to file) can be
substituted with the path of another resource on the same server, effectively
allowing the displaying of arbitrary, and possibly restricted/sensitive, files.

Arachni discovered that it was possible to substitute a parameter value with another
resource and have the server return the contents of the resource to the client within
the response.
},
            references:  {
                'OWASP' => 'https://www.owasp.org/index.php/PHP_File_Inclusion'
            },
            tags:            %w(file inclusion error injection regexp),
            cwe:             98,
            severity:        Severity::HIGH,
            remedy_guidance: %q{
It is recommended that untrusted data is never used to form a file location to
be included.

To validate data, the application should ensure that the supplied value for a file
is permitted. This can be achieved by performing whitelisting on the parameter
value, by matching it against a list of permitted files. If the supplied value
does not match any value in the whitelist, then the server should redirect to a
standard error page.

In some scenarios, where dynamic content is being requested, it may not be possible
to perform validation against a list of trusted resources, therefore the list must
also become dynamic (updated as the files change), or perform filtering to remove
extraneous user input (such as semicolons, periods etc.) and only permit `a-z0-9`.

It is also advised that sensitive files are not stored within the web root and
that the user permissions enforced by the directory are correct.
}
        }
    }
end

.optionsObject


17
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'components/checks/active/file_inclusion.rb', line 17

def self.options
    @options ||= {
        format: [Format::STRAIGHT],
        signatures: FILE_SIGNATURES_PER_PLATFORM.merge(
            # Generic PHP errors.
            php: [
                'An error occurred in script',
                'Failed opening required',
                'failed to open stream:',
                proc do |response|
                    next if !response.body.include?( "Failed opening '" )
                    /Failed opening '.*?' for inclusion/
                end,
                proc do |response|
                    next if !response.body.include?( '<b>Warning</b>:' )
                    /<b>Warning<\/b>:\s+(?:file|read_file|highlight_file|show_source)/
                end
            ],
            perl: [
                proc do |response|
                    next if !response.body.include?( ' line ' )
                    /in .* at .* line d+?\./
                end
            ]
        ),

        # 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

            # 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

.payloadsObject


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'components/checks/active/file_inclusion.rb', line 66

def self.payloads
    @payloads ||= {
        unix:    [
            '/proc/self/environ',
            '/etc/passwd'
        ],
        windows: [
            '/boot.ini',
            '/windows/win.ini',
            '/winnt/win.ini'
        ].map { |p| [p, "c:#{p}", "#{p}#{'.'* 700}", p.gsub( '/', '\\' ) ] }.flatten,
        java:    [ '/WEB-INF/web.xml', '\WEB-INF\web.xml' ]
    }.inject({}) do |h, (platform, payloads)|
        h.merge platform => payloads.map { |p| [p, "file://#{p}" ] }.flatten
    end
end

Instance Method Details

#runObject


83
84
85
# File 'components/checks/active/file_inclusion.rb', line 83

def run
    audit self.class.payloads, self.class.options
end