Web App Security Testing with a Custom Proxy Server
by Nitesh Dhanjani01/22/2004
In this article, I'll discuss some common web-application security flaws and then demonstrate how to detect them. In the process of auditing web applications for security flaws, I will also present a PHP script that will act as a web proxy server, allowing us to intercept and alter HTTP requests between the web browser and the target web server. As we will see, this PHP script will aid us tremendously in testing for security flaws.
Let's start by looking at some common web-application security flaws that can be tested by using a custom web proxy server. The most common security flaw revolves around the sanitization of input, or "input validation."
Input Validation
Consider a login.php script that authenticates access to a web portal. Suppose this script accepts two parameters, username and password, via the HTTP POST method. Embedded in the source code of this script may be an actual authentication check, as follows:
$sqlquery="SELECT * FROM USERS WHERE username='$username'
AND password='$password'";
if(function_to_perform_query($sqlquery))
{
//authenticated!
}
else
{
//wrong username or password. error!
}
Looks pretty simple, doesn't it? Suppose a user were to input admin in the username field of the login form, and the following in the password field:
a' or 'a'='a
Now, the $sqlquery variable will hold the following value:
SELECT * FROM USERS WHERE username='admin' AND password='a' or 'a'='a'
When the above query executes, the user will be allowed to login as admin. The reason for this is pretty obvious. The SQL query will always return true because of the additional or 'a'='a' clause, which is of course always true: a will always be equal to a.
This act of injecting SQL queries into web apps that do not perform proper sanitization of HTTP parameters is known as "SQL injection."
Most programming languages support a function call that allows for the execution of external programs. Many poorly designed web applications sometimes fail to perform input validation on parameters that are passed into calls, such as system(). For example:
system("/bin/echo $i");
If $i were a parameter accepted from the user, then a malicious user may enter the following value:
`cat /etc/passwd`
for the value of $i. This would cause the preceding system call to execute the following:
/bin/echo `cat /etc/passwd`
Thus displaying the /etc/passwd file instead of a static value of $i.
Sanitizing user input can help to prevent input validation vulnerabilities. Web app developers should watch out for the following characters:
` . ; \ / @ & | % ~ < > " $ ( ) { } [ ] * ! '
In addition, to have PHP escape your GET and POST parameter input strings automatically, edit your php.ini file to set the value of magic_quotes_gpc to On. This has further implications, so please review the PHP manual appropriately.
Sessions
Since HTTP is not a stateful protocol, the web application must provide any session management it needs. Many sessions use client-side cookies to maintain state. A client-side cookie is a piece of information requested by the web application to be stored on the end user's hard disk. Since these cookies are stored on user's disks, users or other programs can alter them, so they're untrustworthy. Poorly designed web applications often trust client-side cookies, allowing potential compromises of the web application's authentication mechanism. Consider a cookie with the following data:
lang=en-us; user=joe; time=10:10 EST;
A malicious user may attempt to alter this cookie and place
admin or root in the user field and have access to a
higher privileged account.
A web application may also maintain state by embedding session information in the URL:
http://www.blahwebserver.com/authenticated.cgi?user=john&sessionid=12345678
Often, web apps provide session ids sequentially. This would allow the user
john to brute-force session values of lower than 12345678 to
hijack sessions of users that have logged on before him.
Hidden HTML Elements
These are static elements contained in web forms. These values are usually not displayed by the client, but are merely used to pass information back and forth between different web forms via POST requests. Here is an example of an HTML code that defines a hidden element:
<INPUT NAME="shippingcharges" TYPE=HIDDEN VALUE="5.25">
The problem in this example is that a malicious user may alter the HTML and
submit any value he or she wishes. Often, web applications are designed to
trust client-side input fully. So, in our example, a malicious user may use a
web proxy server (such as the one we will see shortly) to alter the value of
shippingcharges that is submitted to the purchase form. Imagine
what would happen if a user were to submit a value such as -100.
Assuming that the web application blindly accepts this value, the user might
end up with a credit on his credit card!
NOTE: The above is only a list of some web-application security flaws that can be tested using a custom proxy server. It should by no means be considered an exhaustive list of all possible web-application security vulnerabilities.
Our Own Custom Proxy Server
As we have seen, web applications should properly validate user input. In order to test a web application that relies on GET requests to accept data, all you have to do is alter parameter values by changing the values in the URL. This is because, in the case of GET requests, the input parameter values are embedded in the URL itself:
http://www.blahwebserver.com/script.cgi?login=admin&display=true
However, in the case of POST requests, things get a tad bit complicated. Unlike a GET request, the data submitted by a POST request is not displayed in the URL. Here is an example of a POST request sent by the web browser to the web server:
POST /script.cgi HTTP/1.0\r\n
Host: www.blahwebserver.com\r\n
Content-Length: 24\r\n
\r\n
login=admin&display=true
Of-course, you may submit a POST request like the one above by using a telnet client to connect to the web server and typing the request manually, but this
is tedious. To make life easier, we will write a web proxy that will help us
alter POST requests as they are submitted by the web browser. See proxy.php for its source code. In addition to intercepting POST request data, you may want to intercept and alter cookie values present in
the HTTP header. Therefore, if needed, just remove this if
condition in proxy.php and you will be able to alter every request
sent by the browser:
if($ispost==1)
You will need the PHP command-line interpreter to run
proxy.php. Run the script like so:
[bash]$ php -q proxy.php
NOTE: If you get an error about undefined socket functions, your PHP
installation probably does not support PHP's latest socket API. You will need to
recompile your PHP installation by running ./configure with the
following flag:
--enable-sockets
Now, configure your web browser's proxy settings to point to IP address
127.0.0.1 and port 8000. Once you do this, you should be able to browse the web
as usual. Whenever your browser submits a POST request, the
proxy.php script will detect it, allowing you to change the POST
request being submitted in your favorite editor.
Let's explore how proxy.php works. In order to perform as a
server, it binds to a port using the socket_bind and
socket_listen calls within the PHP API. Once this is done, the
script continuously loops for incoming connections, answering each incoming
connection with the socket_accept call, which blocks until a client
connects. Note that our implementation does not provide us with a
multi-threaded proxy server. For the purposes within our scope of testing, it
should suffice.
When a web browser needs to request a resource via a POST request, it will connect to our proxy server and submit a request, such as:
POST http://www.blahwebserver.com/login.php HTTP/1.1\r\n
Host: www.blahwebserver.com\r\n
User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O;
en-US; rv:1.5) Gecko/20031007\r\n
Accept: text/xml,application/xml,application/xhtml+xml,text
/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif
;q=0.2,*/*;q=0.1\r\n
Accept-Language: en-us,en;q=0.5\r\n
Accept-Encoding: gzip,deflate\r\n
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n
Keep-Alive: 300\r\n
Proxy-Connection: keep-alive\r\n
Referer: http://www.blahwebserver.com/login.php\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 25\r\n
\r\n
login=admin&pass=p455w0rd
As soon as it receives such a request, proxy.php will perform
the following steps:
Verify if it is a GET or POST request. A GET request ends with the
\r\n\r\ncharacters. However, if it is a POST request, as in the example above, theContent-Lengthfield will specify the string length of the parameters being passed.proxy.phpdetects this and will therefore expect 25 characters (login=admin&pass=p455w0rd) to follow after the HTTP header that ends after the sequence\r\n\r\n.The request is stored in a temporary file in the
/tmpdirectory. If the request is of type POST, an editor specified by the$editorvariable is spawned. This allows the user to edit the POST request on the fly. Once the user saves this file by exiting out of the editor -- and assuming the user has altered the request to perform SQL Injection by sendinglogin=admin&pass=a' or 'a'='aas the POST data -- the request will be changed to:POST /login.php HTTP/1.0\r\n Host: www.blahwebserver.com\r\n User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.5) Gecko/20031007\r\n Accept: text/xml,application/xml,application/xhtml+xml,text /html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif ;q=0.2,*/*;q=0.1\r\n Accept-Language: en-us,en;q=0.5\r\n Accept-Encoding: gzip,deflate\r\n Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n Referer: http://www.blahwebserver.com/login.php\r\n Content-Type: application/x-www-form-urlencoded\r\n Content-Length: 29\r\n \r\n login=admin&pass=a' or 'a'='aNote that the proxy changes
HTTP/1.1toHTTP/1.0since we don't want to be bothered withkeep-aliverequests that are a part of the HTTP/1.1 specification. It removes theProxy-Connectionline as well. Also notice that the proxy recalculates the value of Content-Length before sending it out to the web server.When the request is sent to the web server,
proxy.phpwaits for it to respond with the results and then immediately writes the response to the socket connection between the web browser and the proxy. After this is done, the socket connection is closed, and the while loop takes the proxy back to listening for more connections from the web browser.
There we are. Our own web proxy server that helps us modify POST requests on-the-fly!
Conclusion
The proxy.php script doesn't handle HTTPS but it can be added by
supporting HTTP's CONNECT requests. Perhaps I will alter
proxy.php and write about it at a later time. In the meantime, those
who don't want to be bothered with writing their own proxy server may want to
take a look at SPIKE Proxy and Paros Proxy.
Nitesh Dhanjani is a well known security researcher, author, and speaker. Dhanjani has been invited to talk at various information security events such as the Black Hat Briefings, RSA, Hack in the Box, Microsoft Blue Hat, and OSCON.
Return to the PHP DevCenter.
Showing messages 1 through 4 of 4.
-
An automated web security gateway can inspect and block attacks
2004-04-29 10:42:28 Roboo [View]
also filter many other things unwanted. Besides HTML Web applicaitons, XML SOAP Web Services can be enabled and protected too. Roboo
-
HTTP Proxy
2004-01-22 20:31:26 Chris Shiflett |
[View]
Your proxy reminds me of a project of mine from a few years ago called Protoscope. At the time, the sockets extension was so volatile that I wouldn't be surprised if it no longer works. You can glance through the source here:
http://cvs.sourceforge.net/viewcvs.py/protoscope/protoscope/protoscope.php?rev=1.30&view=auto
If I recall correctly, it handles chunked responses and a lot of little details like that, but it's still not very robust and was never truly completed. It's nice to see that the same idea has arisen again. -
Working Link
2004-01-22 20:32:44 Chris Shiflett |
[View]
I guess some HTML is necessary here:
http://cvs.sourceforge.net/viewcvs.py/protoscope/protoscope/protoscope.php?rev=1.30&view=auto -
Working Link
2004-01-24 09:24:39 Nitesh Dhanjani |
[View]
Very nice, thanks for sharing!






