Press "Enter" to skip to content

A Possible Security Vulnerability in Nginx + PHP CGI

The usual Nginx + PHP cgi setup sets SCRIPT_FILENAME in the config file with a regex match (The PATH_INFO Problem with Nginx (PHP/fastcgi)). Today Xiaodun found a security hole in this approach.
Say you have http://www.laruence.com/fake.jpg. Craft the following URL and you can see the binary contents of fake.jpg:

http://www.laruence.com/fake.jpg/foo.php

Why does that happen?
Take this nginx conf:

location ~ \.php($|/) {
	fastcgi_pass   127.0.0.1:9000;
	fastcgi_index  index.php;
	set $script    $uri;
	set $path_info "";
	if ($uri ~ "^(.+\.php)(/.*)") {
		set  $script     $1;
		set  $path_info  $2;
	}
	include       fastcgi_params;
	fastcgi_param SCRIPT_FILENAME   $document_root$script;
	fastcgi_param SCRIPT_NAME       $script;
	fastcgi_param PATH_INFO         $path_info;
}

After the regex match, SCRIPT_NAME is set to "fake.jpg/foo.php", which is then built into SCRIPT_FILENAME and handed to PHP CGI. But why does PHP accept that kind of parameter and go on to parse a.jpg?
That brings us to the fix_pathinfo parameter in PHP's cgi SAPI:

; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI.  PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is.  For more information on PATH_INFO, see the cgi specs.  Setting
; this to 1 will cause PHP CGI to fix it's paths to conform to the spec.  A setting
; of zero causes PHP to behave as before.  Default is 1.  You should fix your scripts
; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
cgi.fix_pathinfo=1

If this option is enabled, the following logic in PHP kicks in:

/*
 * if the file doesn't exist, try to extract PATH_INFO out
 * of it by stat'ing back through the '/'
 * this fixes url's like /info.php/test
 */
if (script_path_translated &&
	(script_path_translated_len = strlen(script_path_translated)) > 0 &&
	(script_path_translated[script_path_translated_len-1] == '/' ||
....//omitted below.

At this point PHP believes SCRIPT_FILENAME is fake.jpg and foo.php is PATH_INFO, so it happily interprets fake.jpg as a PHP file... So...
In Xiaodun's words, the harm this hides is enormous.
For a forum, all it takes is uploading an image (which is really a malicious PHP script) and then crafting a request like the one above...
So if you run this server combination, please check. If you are exposed, turn off fix_pathinfo (it is on by default).
For the full vulnerability details, head over to Xiaodun's BLOG: 80Sec
One more thing: I don't think this has much to do with Nginx, and it is not an Nginx vulnerability. It is a configuration problem, yet everywhere people are calling it an Nginx Bug. Not right, not right.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.