XSS is just another way that unfiltered input can bite. A unique property of XSS is that a vulnerable web server rarely compromises the security of it's own data (or data stored within), rather it exposes it's guests to a number of attacks. The most common is cookie theft or redirection of the client browser to content on sites that it wouldn't normally trust.
To demonstrate the cookie theft scenario, I created two small scripts, vuln.php and check_cookie.php, and placed them on a web server. The two pages mimic a web application that relies on cookies to identify and authenticate a remote user. In the real world, a server may not create a cookie until after a user logs in (or it will modify the cookie after successful login), however in this case, the cookie is created upon first accessing vuln.php. The value of the cookie is a a pseudo-randomly generated number between 0 and 99999999 and is valid for 10 hours.
# vuln.php <? setcookie("vulncookie", rand(0,99999999), time()+36000); $nf = $HTTP_GET_VARS['nf']; print "<html><body>"; print "$nf"; print "</body></html>"; ?> # check_cookie.php <? if (isset($_COOKIE["vulncookie"])) echo "Welcome " . $_COOKIE["vulncookie"] . "!"; else echo "You are not logged in!"; ?>
This is obviously a very simple example, but the vulnerable code is in vuln.php – in particular the use of $nf. This variable is not filtered in any manner before printed on the page back to the client. If a client wanted to display the word “hello” on the page in bold font, it could construct a URL like the one shown in this screen shot:
At this point, the client has control over what type of content is displayed on the page. In a sense, it has put words into the server's mouth. Behind the scenes, the client has obtained a session cookie from the server, which it will need when visiting check_cookie.php. To understand the danger, imagine vuln.php is a login page that sends the client a cookie upon successful authentication and check_cookie.php is the user's email inbox. As long as the cookie's lifetime hasn't expired, and the legitimate user doesn't expire it manually by logging out, another user could steal it for his own use. So, how does the unfiltered $nf variable help an attacker get access to the cookie?
A small variant of this URL could force the client to send it's cookie value to a CGI script on a remote web server. The attacker would either monitor access logs on the remote web site, or he would program the CGI script so that it emails him the results or engages in an automated type of attack (ie fetches contents of the user's inbox and saves them for later viewing). To simulate this, a third page, take_cookie.php, will be introduced.
# take_cookie.php <? $stolen_cookie = $_SERVER['QUERY_STRING']; $remote_address = $_SERVER['REMOTE_ADDR']; $file = fopen("/tmp/collection.txt","w+"); fwrite($file, "Remote IP $remote_address has cookie $stolen_cookie"); fclose($file); ?>
All the attacker has to do now is create a valid URL with his payload and entice the legitimate user to click it. This can be accomplished via email or forum post, accompanied with some social engineering tricks. An example would be something like this:
Notice in this case, take_cookie.php is on the same web server as vuln.php – but this is only an example. In real attacks, take_cookie.php will be on a server owned by the attacker, or one he has compromised to further hide his tracks. After a legitimate user clicks the URL, an attacker would have obtained the following record in collection.txt:
Remote IP 192.168.1.1 has cookie vulncookie=97097015
Once again, this is just a very simple example. No scripts on the server side check validity of the cookie, only that the client offers one with the same name (vulncookie). In other words, a real check_cookie.php script would validate that the cookie's value was a number the server indeed issued to a client. For a quick look at what the attacker can do with the cookie, we can use wget against check_cookie.php from a new machine. This new machine has never had any contact with the web application server.
Recall from the program's source code above that it determines if a cookie is set and has the proper name. If so, the client is allowed to pass. If not, it displays “You are not logged in!”
# wget http://monitor.mnin.org/check_cookie.php # cat check_cookie.php You are not logged in!
As expected, the user is denied access, because it does not have a cookie. Now, look what happens if the cookie is included in the request:
# wget http://monitor.mnin.org/check_cookie.php --header='Cookie: vulncookie=97097015' # cat check_cookie.php Welcome 97097015!