CVV #1: Local File Inclusion

This is a short series about “Common Vulnerability Vectors” and related exploitation-methods.

Just a pointless image

I’m gonna start this series with a quite known and therefore old vector: Local File Inclusion (short: “LFI”).

According to Wikipedia, “LFI” is described as:

A type of “File Inclusion Vulnerability”, […] that is most commonly found to affect web applications that rely on a scripting runtime […], local files i.e. files on the current server can be included for execution.”

There is also a short example (written in PHP) which describes the vulnerability (and it’s basic form) very well:

include($_GET['filename'] . '.php');

This example can be easily exploited by using Path Traversal for navigation and inclusion of a sensitive or useful file, in this example /etc/passwd/ based on the assumption that the server’s OS is based on Linux:

GET vulnerable.php?filename=../../../etc/passwd HTTP/1.1

HTTP Response:


Although this type of vulnerability is very old, if found, there is a very likely chance to expand the “LFI” to a Remote Code Execution. So: Try to remember “LFI” when testing functions related to file-handling like templates, attachments, requests, read or write operations.

The following content describes methods based on my current knowledge who might be useful when expanding a “LFI” to a “RCE”.

Thanks to: _lavalamp, smiegles, arr0way, d.w., swisskey, rawsec_cyber, j.adriaans for contributing their knowledge to this topic.

[1] Path Truncation

PHP by default handles /etc/passwd like /etc/passwd/ or /etc/passwd/// or /./etc/passwd, trailing slashes are stripped of before opening the file. On the most PHP installations a filename longer than 4096 bytes will be cut off so any excess chars will be thrown away. This allows to bypass a hard-coded file-extension by simply pushing the parameter with trailing slashes over it’s size:

GET vulnerable.php?filename=../../../etc/passwd/././././././././/././././././././././[ and so on ] HTTP/1.1

[2] Nullbyte Injection

An URL-encoded nullbyte %00 can be used on PHP ≤ v.5.3. to cut off a hard-coded file-extension. This is possible due PHP’s relationship to C. In C a nullbyte represents the end of a string, therefore all chars after the nullbyte will be ignored.

GET vulnerable.php?filename=../../../etc/passwd%00 HTTP/1.1

[3] /proc/self

Because Linux-systems are using the file-structure for almost everything, the environment-variables of the current process (self) can be accessed via /proc/self/environ. One of the environment-variables set (if apache2 is running) is the user-agent which can be controlled through a HTTP request. In this case “RCE” can be achieved by requesting the file in combination with the payload written into the HTTP User-Agent field.

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

Similarly /proc/self/fd/<id> (or it’s symlink: /dev/fd) can be used in combination with the HTTP Referer field to inject the payload into opened error-logs by apache2. Although it’s needed to brute-force these ids first to determine currently active file-descriptors referring to the opened file.

[4] PHP wrapper

There is a handful of PHP wrappers who can access different I/O or data streams via the PHP daemon and can (if enabled; allow_url_include) lead to a direct execution of instructions. php://input is a read-only stream that allows reading raw data from the request body. In this case, it is possible to inject code via POST-request:

POST vulnerable.php?filename=php://input HTTP/1.1
<?=system('ls'); ?>

php://filter is a kind of meta-wrapper designed to permit the application of filters to a stream at the time of opening. It can be used to read the content of a PHP file (which gets interpreted when included the natural way):

GET vulnerable.php?filename=php://filter/convert.base64-encode/resource=../vulnerable.php HTTP/1.1

In this case, the response is Base64 encoded (there are also other variants for returning the response e.g. ROT13 encoded: filter/read=string.rot13/resource=../vulnerable.php).

If the vulnerable application accepts file-uploads the zip:// or phar:// (PHP archive format) wrapper can be used for pointing to the uploaded and compressed version of the payload and unpacking it via a direct-path declaration:

zip payload.php
GET vulnerable.php?filename=zip:// HTTP/1.1

The wrapper doesn’t need a .zip file-extension for unpacking and interpreting the data (a renamed ZIP-archive can also be used).

[5] Log poisoning

Web-servers like Apache or Nginx are logging user-requests or application-related errors (like a stack-trace) to specific log-files on the server. These files can be manipulated by e.g. requesting HTTP Status 404 (a not-found page) with the payload via some HTTP method. Beware! The inclusion of these files can crash the browser (log-files can be big).

GET vulnerable.php?filename[]= HTTP/1.1
Referer: <?=phpinfo();?>
GET vulnerable.php?filename=../../../var/log/nginx/error_log HTTP/1.1

It is also possible to use the SSH authentication (similar authentication schemes like FTP are working too) to deliver and execute a payload by connecting to the SSH service:

ssh <?=phpinfo();?>@<ip-of-vulnerable-target>
GET vulnerable.php?filename=../../../var/log/auth.log HTTP/1.1

[6] PHP Sessions

PHP stores it’s user-session in files located under /var/lib/phpX/sess_<PHPSESSID> (where x=Version). If there is any function on the application like a login that pushes data into the current user-session file (e.g. a username for reflecting it later), this function can be abused to save the payload into the current session-file.

POST login.php HTTP/1.1
GET vulnerable.php?filename=../../../var/log/nginx/error_log HTTP/1.1

[7] phpinfo()

The output of the phpinfo() function contains the values of the PHP variables, including any values set via a POST, GET or FILE request. This can be used to debug the interpreter’s behavior by e.g. uploading a file which gets stored temporary in /tmp/<random> and is deleted at the end of the call. By making multiple upload posts to the function and carefully controlling the reads, it is possible to retrieve the name of the temporary file and make a request, specifying the temporary filename. “LFISuite” created by D35m0nd142 allows to exploit this complex method:

[8] E-Mail

If the server is running a mail service it is possible to send the payload via mail and including it the associated log under /var/log/<user> (every other user has it’s own file).

mail -s "<?=phpinfo();?>" < /dev/null
GET vulnerable.php?filename=../../../var/log/www-data HTTP/1.1

I hope you learned some new things. If you found any content-related or grammatical errors or have an addition, write a comment.