How to painlessly defuse the CGI exploit.
| From: | Preston L. Bannister | Date: | Sat, 11 May 2002 02:00:36 +0000 |
| Subject: | How to painlessly defuse the CGI exploit. | ||
| References: | 1 | Groups: | php.cvs |
| Request: | Send a blank email to php-cvs+get-11813@lists.php.net to get a copy of this message | ||
Let's summarize.
Placing an interpreter (PHP, Perl, AWK, sh, or even "cat") within
the web tree is a very bad idea.
The recommended configuration for PHP CGI with Apache places the
PHP executable within the web tree. Apparently with Apache there
is no way(?) to configure the PHP interpreter outside the web tree.
To prevent the classic CGI exploit made possible by the recommended
Apache configuration, an Apache specific test was added to PHP.
The test to match the problem could have been:
IF (server is Apache) AND (REQUEST_STATUS not set)
THEN (emit warning)
This is an Apache-specific hack (checking REQUEST_STATUS) to address
an Apache-specific problem caused by the recommended configuration.
Adding a bit of code specific to Apache is not generally a bad idea
given that about half the web servers in the world are Apache :).
The actual test screws up IIS (and other non-Apache servers):
IF (REQUEST_STATUS not set)
THEN (emit warning)
Unfortunately this is causing a lot of confusion. Google lists
about 250-odd references to the warning - not bad for a bit of code
only out a few months :).
How can we solve the problem with less grief?
Simple - don't place the PHP executable in the web tree!
Place a "php.bat" in the Apache cgi-bin directory containing commands:
@echo off
c:\php\php.exe
The Apache configuration then becomes:
AddType application/x-httpd-php .php .phtml
Action application/x-httpd-php "/cgi-bin/php.bat"
Note the absence of the ScriptAlias statement - no PHP in the web tree!
To see why this works modify "php.bat" slightly:
@echo off
echo.
set
echo.
echo arg1 = %1
echo arg2 = %2
echo arg3 = %3
echo.
echo ... PHP output
echo.
c:\php\php.exe
This gets you a blank line (to start body), the environment, the
first three command line arguments, and finally the PHP output.
Lets try the exploit:
http://gerald:3000/cgi-bin/php.bat?/small.txt
Yields the response:
----------------------------------------------
COMSPEC=C:\WINDOWS\COMMAND.COM
DOCUMENT_ROOT=c:/program files/apache group/apache/htdocs
HTTP_ACCEPT=*/*
HTTP_ACCEPT_ENCODING=gzip, deflate
HTTP_ACCEPT_LANGUAGE=en-us
HTTP_CONNECTION=Keep-Alive
HTTP_HOST=gerald:3000
HTTP_USER_AGENT=Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; Q312461)
PATH=C:\PERL\BIN\;C:\WINDOWS;C:\WINDOWS\COMMAND;C:\BIN;C:\PERL\BIN;C:\CYGWIN\BIN;C:\PROGRA~1\MICROS~1\COMMON\MSDEV98\BIN;C:\PROGRA~1
\MICROS~1\VC98\BIN;C:\PROGRA~1\MICROS~1\COMMON\TOOLS\WIN95;C:\PROGRA~1\MICROS~1\COMMON\TOOLS;\SYSTEM;;\COMMAND;C:\ADABAS\BIN;C:\ADAB
AS\PGM;C:\PROGRAM FILES\MTS
REMOTE_ADDR=192.168.1.102
REMOTE_PORT=1144
SCRIPT_FILENAME=c:/program files/apache group/apache/cgi-bin/php.bat
SERVER_ADDR=192.168.1.102
SERVER_ADMIN=admin@zotez.com
SERVER_NAME=gerald.zotez.com
SERVER_PORT=3000
SERVER_SIGNATURE=<ADDRESS>Apache/1.3.24 Server at gerald.zotez.com Port 3000</ADDRESS>
SERVER_SOFTWARE=Apache/1.3.24 (Win32)
WINDIR=C:\WINDOWS
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
REQUEST_METHOD=GET
QUERY_STRING=/small.txt
REQUEST_URI=/cgi-bin/php.bat?/small.txt
SCRIPT_NAME=/cgi-bin/php.bat
arg1 = "/small.txt"
arg2 =
arg3 =
... PHP output
X-Powered-By: PHP/4.2.0
Content-type: text/html
No input file specified.
----------------------------------------------
For reference try an acceptable request:
http://gerald:3000/php/env.php
Yields the response:
----------------------------------------------
COMSPEC=C:\WINDOWS\COMMAND.COM
DOCUMENT_ROOT=c:/program files/apache group/apache/htdocs
HTTP_ACCEPT=*/*
HTTP_ACCEPT_ENCODING=gzip, deflate
HTTP_ACCEPT_LANGUAGE=en-us
HTTP_CONNECTION=Keep-Alive
HTTP_HOST=gerald:3000
HTTP_REFERER=http://gerald:3000/php/
HTTP_USER_AGENT=Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; Q312461)
PATH=C:\PERL\BIN\;C:\WINDOWS;C:\WINDOWS\COMMAND;C:\BIN;C:\PERL\BIN;C:\CYGWIN\BIN;C:\PROGRA~1\MICROS~1\COMMON\MSDEV98\BIN;C:\PROGRA~1
\MICROS~1\VC98\BIN;C:\PROGRA~1\MICROS~1\COMMON\TOOLS\WIN95;C:\PROGRA~1\MICROS~1\COMMON\TOOLS;\SYSTEM;;\COMMAND;C:\ADABAS\BIN;C:\ADAB
AS\PGM;C:\PROGRAM FILES\MTS
REDIRECT_STATUS=200
REDIRECT_URL=/php/env.php
REMOTE_ADDR=192.168.1.102
REMOTE_PORT=1144
SCRIPT_FILENAME=c:/program files/apache group/apache/cgi-bin/php.bat
SERVER_ADDR=192.168.1.102
SERVER_ADMIN=admin@zotez.com
SERVER_NAME=gerald.zotez.com
SERVER_PORT=3000
SERVER_SIGNATURE=<ADDRESS>Apache/1.3.24 Server at gerald.zotez.com Port 3000</ADDRESS>
SERVER_SOFTWARE=Apache/1.3.24 (Win32)
WINDIR=C:\WINDOWS
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
REQUEST_METHOD=GET
QUERY_STRING=
REQUEST_URI=/php/env.php
SCRIPT_NAME=/cgi-bin/php.bat
PATH_INFO=/php/env.php
PATH_TRANSLATED=c:\inetpub\wwwroot\php\env.php
arg1 =
arg2 =
arg3 =
... PHP output
X-Powered-By: PHP/4.2.0
Content-type: text/html
[snipped valid output from PHP script]
----------------------------------------------
Note two interesting differences between the two requests.
The exploit passes one command line argument, and does *not* set either
PATH_INFO or PATH_TRANSLATED.
The valid request passes *no* command line arguments, and *does* set
PATH_INFO or PATH_TRANSLATED.
Since the cgi-bin/php.bat ignores the command line argument, the exploit
is defeated.
(BTW, I didn't invent this solution - it came from a Perl FAQ :).
--
Preston L. Bannister
http://members.cox.net/preston.bannister/
pbannister on Yahoo Messenger