Definition | A script is a small program written in an interpreted language such as PHP. A script can run on a server or on a client. |
As I told you earlier, the cause of holes or vulnerabilities in Web applications is dynamic content, that is, different responses of scripts to different external conditions. By external conditions I mean data sent by a client or a browser using HTTP. Therefore, you should know how a client can send data, how HTTP regulates sending data to the server, and what the differences are among various data-sending methods.
The HTTP GET method is the most popular and the simplest method of sending data from a client to the server.
Definition | GET is a method for sending data using HTTP. According to this method, data are preceded by the address of the requested page and a question mark. |
GET parameters can be edited by editing the address of the requested page. They are delimited with ampersands, and the name of a parameter is separated from its value with an equal sign.
For example, in the http:/localhost/2/1.php?test=hello&id=2 request, two GET parameters are sent to the http://localhost/2/1.php script. The first is test with the hello value, and the second is id with the 2 value.
The GET method sets the maximum length of the sent data. You cannot use GET to send files.
Here are a few examples of how data are sent as GET parameters.
The simplest way to create a request to a script involves using the <a> HTML tag:
<a href=http://localhost/2/1.php?id=21&test=hello>Test1 </a>
<a href=1.php?id=21&test=hello>Test1 </a>
<a href=/2/1.php?id=21&test=hello>Test1 </a>
In the first request, two parameters, id = 21 and test = hello, are sent to the http://localhost/2/1.php script using the HTTP GET method.
In the second request, the same parameters are sent to the 1.PHP script located in the same directory on the same server as the current script.
In the third request, these parameters are sent to the /2/1.php script on the same server. The absolute path to the script from the root directory is specified.
Another example is the use of a form to send data:
<form action=http://localhost/2/1.php method=GET>
id: <input type=text name=id>
test: <input type=text name=test>
<input type=submit>
</form>
If the action parameter isn't specified in the form header, the data will be sent to the current script. If the method parameter is omitted, the default method is GET.
In this example, two GET parameters are sent to the http://localhost/2/1.php script.
Look at data sent from a client to the Web server when the GET method is used. Here is an actual header sent by Mozilla 1.7.1. in the Windows 2000 operating system:
GET /2/1.php?id=21&test=hello HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.0; en-US; rv:1.7.1)
Gecko/20040707
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 3000
Connection: keep-alive
The first line is the most interesting. The first word in the first line is the method used to send the data. It is followed by the script address (relative to the server root) and the protocol. In this example, HTTP/1.1 is used.
The second line specifies the name of the server whose script is requested.
In the third line, the browser identifies itself. You can see the type of the browser, the operating system, and the browser version.
The next lines contain information about the types of documents the browser "understands," the language and encoding it "prefers," and the allowed types of data compression.
The last two lines tell the server that it shouldn't disconnect after it sends the requested document but should keep the connection for the specified time.
Detailed knowledge of header fields sent during a GET request will allow you to simulate HTTP sessions, that is, write programs that can request documents on a server but cannot be differentiated from a common browser.
Another method for sending data using HTTP is POST.
Definition | POST is a method for sending data using HTTP. With this method, data are sent after all headers are sent from a client to the server. |
You can send data with the POST method from an HTML page only using a form. The syntax of the form is identical to the form for the GET request except that the POST method is specified:
<form action=http://localhost/2/1.php method=POST>
id: <input type=text name=id><br>
test: <input type=text name=test><br>
<input type=submit>
</form>
In this example, two parameters, id and test, are sent to the specified script using the HTTP POST method.
If the action parameter isn't specified in the form header, the data will be sent to the current script. The value of the action parameter can be shortened. If no server is specified, the data will be sent to the current server. If you don't specify the server and the path, the data will be sent to the script in the same directory on the same server.
Look at data sent from a browser when the HTTP POST method is used. Here is an actual header sent by Mozilla:
POST /2/1.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.0; en-US; rv:1.7.1) Gecko/20040707
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 3000
Connection: keep-alive
Referer: http://localhost/2/1.php?id=21&test=hello
Content-Type: /x-www-form-urlencoded
Content-Length: 16
<empty line>
id=53&test=hello
As you can see, this request differs from an HTTP GET request. It begins with the POST word, telling the server that it should wait for POST parameters.
Like in the GET method, the method name is followed by the address of the requested script, the protocol (HTTP/1.1), the server, the browser identifications, the type of pages, and so on.
Both the GET and the POST method can include the Referer field. The value of this field is the address of the last page visited by the user.
The Referer field is followed by two fields, Content-Type and Content-Length. Although the GET request to the server had only the header and didn't have a body (i.e., contents), the contents of the POST request are the data sent with the POST method. Therefore, two header fields are necessary.
Content-Type is the type of data sent within the body. In this example, the value of this field is application/x-www-form-urlencoded. It indicates that the body contains data that are uniform resource locator (URL) encoded from a World Wide Web (WWW) form.
Content-Length is the length of the data. This parameter is required so that the server can detect the end of the data.
The empty line indicates the end of the header. It is followed by the POST parameters.
URL encoding means that certain characters are encoded to avoid collisions. For example, suppose that you need to send the text variable with the "help&x=y" value using the POST method. What will happen if you send the data without URL encoding (i.e., text=help&x=y)? The script will parse this sequence as two parameters, text=help and x=y, rather than one variable text with the "help&x=y" value.
To avoid similar collisions when data are sent using the HTTP GET or POST method, certain characters are encoded in a special way. A character is substituted with a sequence of the %XX form. Here, xx is the two-character hexadecimal code of the character being encoded.
For example, the & character is encoded as %26, and the = character is encoded as %3D. The % character is substituted with %25, and so on, for many control characters. In general, you can encode all characters you're sending. However, it is common to encode only necessary characters because this operation increases the size of the data.
Therefore, the string in this example should be encoded as help%26×%3Dy. The text=help%26×%3Dy parameter will be sent to the server, and the script will parse it correctly.
The POST method allows you to send files.
Now, when you know the format of requests sent to the server using the HTTP GET and POST methods, you might be asking, What if I combine these two methods?
What will happen if the following request is sent to the server?
POST /2/1.php?id=88&test=tested HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.0; en-US; rv:1.7.1) Gecko/20040707
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 3000
Connection: keep-alive
Referer: http://localhost/2/1.php?id=21&test=hello
Content-Type: application/x-www-form-urlencoded
Content-Length: 16
<empty line>
id=53&test=hello
On the one hand, the POST request type is specified; on the other hand, some parameters are sent to the script as if they were GET parameters.
It turns out that PHP and many other interpreters of scripts adequately respond to such unusual requests. Parameters sent in such a way are registered as GET parameters. For example, you can access these parameters in PHP with the $_GET global variable. The data sent as POST parameters are registered as you would expect: In PHP, you can access them with the $_POST global variable.
This request to the server was made intentionally. Can you make your browser send such a request? As you know, only one data-sending method can be specified in a form.
A logical approach to this question would involve creating the following form:
<form action=http://localhost/2/1.php?id=88&test=tested method=POST>
id: <input type=text name=id><br>
test: <input type=text name=test><br>
<input type=submit>
</form>
This form is implemented in the http://localhost/2/1.php script. You can easily make sure that the browser displays and submits this form correctly. What's more, it creates a request almost identical to the one created manually.
You might be wondering about the purpose of these manipulations. Consider another example, http://localhost/2/2.php. Here is the source code of this script:
<?
if(empty($_GET["id"]) || (string) (int)$_GET["id"] <> $_GET["id"] )
{
echo "
<form method=GET action=2.php>
Enter ID: <input type=text name=id>
<input type=submit>
</form>
";
exit;
}
mysql_connect("localhost", "root", "");
mysql_select_db("book1");
$q=mysql_query("select * from test1 where id=$id");
if($r=mysql_fetch_object($q))
echo $r->name;
else echo "Records not found";
?>
This is a modified script from Chapter 1. A person's ID is assumed to be a GET parameter, and the script checks whether the id parameter is an integer. Then the automatically registered variable $id is used. Suppose that the PHP interpreter is configured so that POST parameters have the highest priority. When the script receives a GET request, it works reliably.
Imagine a situation, in which POST parameters are sent in addition to GET ones. For example, send the following form (http://localhost/2/form1.html):
<form method=POST action=2.php?id=2>
id: <input type=text name=id>
<input type=submit>
</form>
In this example, the browser creates an HTTP POST request to the 2.PHP script with the id GET parameter equal to an integer, but the POST parameter is requested from the user.
As you can see from the code of the 2.PHP script, the id GET parameter is filtered properly, and control is passed to the block of code that makes a query to a database.
This piece of code uses the automatically-registered value of the id parameter, which is taken from POST parameters in accordance with the PHP interpreter configuration. So, GET parameters are filtered, but POST parameters are used in the query. In other words, you can bypass filtration and insert any data into the database query.
To check this conjecture, send ID values that aren't integers. You'll receive the following message:
Warning: mysql_fetch_object(): supplied argument is not a valid MySQL
result resource in x:\localhost\2\2.php on line 17
records not found
The text of this message allows you to infer that the script has a vulnerability that can be exploited by sending GET and POST parameters in the same request.
Methods for exploiting this vulnerability are described in Chapter 3 devoted to SQL injection.
This is an example demonstrating a rare situation, in which different pieces of a script explicitly or implicitly use variables whose values were obtained from different types of requests. HTTP describes a few other types of requests. However, they aren't popular in the Web, and their description is beyond the scope of this book.
Another popular method of data exchange between the client and the server is the use of HTTP COOKIE parameters.
Definition | Cookies are data stored on the client in small files or in the computer memory. |
COOKIE parameters are sent within the header. The server sends a cookie in the response header, and the client sends it in the request header.
Here is an example of a server response header, in which the server sets the test cookie variable to the hello value:
HTTP/1.1 200 OK
Date: Thu, 01 Sep 2004 12:00:00 GMT
Server: Apache/1.3.12 (Win32)
X-Powered-By: PHP/4.3.3
Set-Cookie: test=hello
Set-Cookie: id=88
Keep-Alive: timeout=15, max=100
Connection: keep-alive
Transfer-Encoding: chunked
Content-Type: text/html
Like in any server response, the first line contains the protocol version followed by a code and its explanation. Response codes and their exploitations can be found in the HTTP specification. Here are a few of them:
200 — The document is on the server. The server should return the document.
301 — The document is removed. The Location field with a new path is expected. The browser should request the document from the new location without saving the current document in the history.
302 — The document is temporarily removed. The Location field with a new path is expected. The browser should request the document from the new location without saving the current document in the history.
401 — Authorization is required. After receiving this response, most browsers display a form suggesting that the user enter a name and a password. These will be sent in the next request.
403 — Access is denied. This doesn't imply that the document is missing from the server.
404 — The document isn't found on the server.
500 — An internal server error. It occurs when there is a collision between a common gateway interface (CGI) program and the server. For example, a Perl script didn't return the expected Content-Type: text/html header. This can happen when an error message is displayed before the header is output. This response code can be an indication of an error in the script.
The second line contains the date and time in Greenwich Mean Time (GMT).
The Server field identifies the server. This information can be interesting to an attacker who can try to find vulnerabilities in a particular version of the server. This is why the system administrator should bar the output of the full server version. He or she can output only the server name or even a random string. For example, in the Apache server configuration the administrator should edit or add the following string:
ServerTokens ProductOnly.
Configuring other types of HTTP servers is beyond the scope of this book.
The x-Powered-By: PHP/4.3.3 field indicates that the page is generated by a PHP script. The PHP interpreter version is output. This information can be useful to the attacker, so the system administrator should configure the PHP interpreter so that it doesn't generate this header. To do this, the administrator would add the expose_php = Off line to the PHP.INI configuration file.
In the next line, the Set-Cookie: test=hello field sets the cookie variable test to the hello value.
As you can see, this field can be repeated for other variables. After the cookie values are set, the browser will send them every time it requests a script from the current (or higher) directory during the current session.
The server can URL-encode cookies.
Here is an example, in which the browser sends the server two cookies set earlier:
GET /2/3.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.0; en-US; rv:1.7.1) Gecko/20040707
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 3000
Connection: keep-alive
Cookie: test=hello; id=88
Cache-Control: max-age=0
As you can see, the values are sent in one header field. They are separated with a semicolon, not with an ampersand as in a POST or GET request. The names and values of these parameters are URL-encoded.
Each COOKIE parameter has a name and a value. In addition, it can include the server address and the path to scripts that require the cookie value. When these are specified, the browser should send the cookies only to documents located in the specified directory or its subdirectories.
Here are examples of cookies with the path and the domain specified:
Set-Cookie: b=tested; path=/2/
Set-Cookie: c=tested; path=/2/; domain=localhost
In the first line, the cookies will be sent only to documents in the /2/ directory (in the current domain) or its subdirectories. In the second line, the domain is specified explicitly.
The server and the path can differ from the current ones. At present, cookies are seldom set to a third-party server. However, in some cases this option can be used by the attacker. The idea of such an attack would be the following: A user who has nothing in common with the attacker visits an intentionally-generated malicious HTML page. Then he or she visits a target server and sends the server certain COOKIE parameters fabricated by the attacker.
As a variant, the attacker can use JavaScript to redirect the visitor to the target server. With other vulnerabilities, this one can be useful to the attacker.
Another parameter that can be sent with a cookie is its lifetime. A cookie shouldn't be stored on the client forever. Its lifetime can be specified when the cookie is set. For example, the following header sets a cookie and specifies its expiration date:
Set-Cookie: a=tested; expires=Thu, 01-Sep-06 00:00:00 GMT
If no lifetime is specified, the cookie lives within the current session until the user exits the browser. In most cases, such cookies are stored in the memory.
If the lifetime is specified, the cookie value is written onto the hard disk. Different browsers store cookies differently. For example, Mozilla stores all cookies in one file, COOKIES.TXT. Internet Explorer stores each cookie in an individual file.
You can draw two conclusions from this. First, a cookie can remain even after the computer is rebooted. It can be repeatedly sent to the server for years until the user deletes it. Second, the user can edit cookie files as he or she likes.
Therefore, I give you the following recommendation: For cookies, set only those parameters that are useless to the attacker. It is likely that the attacker analyzing your Web application for vulnerabilities will examine cookie files and decide whether he or she can benefit from changing their values.
Storing data in hidden fields is a simple and useful option for a programmer.
Definition | A hidden field is a field of an HTML form that isn't displayed on the HTML page containing the form. However, the contents of this field can be seen in the text representation of the HTML page. |
If you use hidden fields when writing Web applications, you should be aware that the attacker can easily read their contents. The attacker is likely to analyze the names and values of the received hidden fields. As practice shows, this analysis can be quite fruitful.
In addition, the attacker can easily change the contents of hidden fields. A script often filters visible parameters but assumes the user cannot change hidden values.
Now that you know a lot about HTTP, you can try to write a small PHP script simulating an HTTP session.
The simplest way to simulate it involves using the fopen() or file_get_contents() function. If you pass either of these functions the name of a remote document with HTTP, and you have appropriate access rights, you'll open this document like a local file.
However, you won't be able to make a POST request, send COOKIE parameters, or send a request through a proxy server. So, it would be best to write a script that uses sockets to create connections, pretends to be a browser, and imitates all header fields, including GET and POST parameters, cookies, and the Referer.
Here is the code of such a script:
01 <?
02 $host="localhost"; // The host
03 $method="POST"; // GET or POST
04 $addr="/2/1.php?id=55"; // The path relative to the server root
05 $useragent="Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)";
07 // The browser identification: IE 5
08 $referer="http://any.com/";
09 $postvars="test=tested"; // POST parameters only for a POST request
10 $cookie="cookvar=hello"; // COOKIE parameters, if any
11 $target="127.0.0.1"; // The IP address of the server or a proxy server
12 $targetport=80; // The port of the server or a proxy server
13 $forwarded="127.0.0.2"; // The value of the X-FORWARDED-FOR header
14 $in = "$method $addr HTTP/1.1\r\n".
15 "Accept: */*\r\n".
16 "Accept-Language: en-us\r\n".
17 "Accept-Encoding: gzip, deflate\r\n".
18 "User-Agent: $useragent\r\n".
19 "Host: $host\r\n".
20 "Connection: Close\r\n";
21 if(!empty($forwarded)) $in.="X-FORWARDED-FOR: $forwarded\r\n";
22 if(!empty($referer)) $in.="Referer: $referer\r\n";
23 if(!empty($cookie)) $in.="Cookie: $cookie\r\n";
24 if($method=="POST")
25 {
26 $len=strlen($postvars);
27 $in.=
28 "Content-Type: application/x-www-form-urlencoded\r\n".
29 "Content-Length: $len\r\n\r\n".
30 $postvars;
31 }
32 $socket = socket_create (AF_INET, SOCK_STRERM, 0);
33 $result = socket_connect ($socket, $target, $targetport);
34 socket_write($socket, $in, strlen($in));
35 $o="";
36 while ($out = socket_read ($socket, 2048)) {
37 $o.=$out;
38 }
49 echo $o;
40 ?>
This script is available on the accompanying CD-ROM in the http://localhost/2/http.php file. This script requests the http://localhost/2/1.php script already familiar to you and displays the received result with all headers in the browser window.
The line numbers are given for your convenience. Lines 02 to 13 define all parameters that will be used in the HTTP request. In this case, it is a POST request that simulates a request made by Microsoft Internet Explorer.
Note the X-FORWARDED-FOR, header. If you connect using a proxy server, it can send the HTTP server the actual Internet protocol (IP) address of the client within this header. Therefore, if you send this header to the server, you'll be able to cheat scripts that will believe the address you send is your IP address. Some popular forums will store this address.
Although system log files on the server contain the actual IP address, sometimes this feature can be useful to the attacker.
If server scripts, like some forms, check the value of the Referer header, you can send a desired Referer header. In addition, you can send cookie data.
The header of the HTTP request is created in lines 14 to 31. In lines 32 to 38, the request is sent to the server, and the output is accumulated in the $o variable. In the next line, the content of the variable is displayed to the browser.
Creating and editing such a script for each request would be a tedious job when you need to send many requests.
You might need to change parameters of the header and the pages. A solution to this problem could be the use of a proxy server, which would change information passing through it according to a certain algorithm.
An example of such a proxy server is Proxomitron. I'm not going to describe its settings here; I just mention that this application fits to this purpose. The application comes with a comprehensive description.
댓글,