Google XSS Game Explained

Cross-site scripting or XSS has been one of those vulnerabilities in security that I am aware of and can exploit with a lot of luck but never really understand the ins and outs. Being that I’m tired of guessing and checking to exploit these vulnerabilities we are going to start with the basics and try our best to fully understand how to solve these basic challenges hosted by Google.

Level 1: Hello, world of XSS

Being that this is a search result and we are requesting information from the server; not altering or adding to the server this attack would be classified as a reflected XSS attack or a non-persistent. In order for this attack to be successful against a particular target a hacker would send a malicious link of the legitimate website with the malicious payload in the url. This can also be done by convincing a person to enter the payload themselves into the vulnerable input field or wherever else is vulnerable.

First things first let’s try any input in order to see how the search uses our query. By inputting “test” into the search field and hitting enter we see the url updates to the following.

Looking at the html of the query we see something of importance. This form has the method “GET” so by submitting the form we will commit to an HTTP GET request with the input id of “query”, the default value “Enter query here…” and onfocus will set the value to “” or nothing when we click into the input field. Then there is another input id for the button which is what we will use to submit. We see no functions being called within the form that would tell us there is input validation here but, it may be elsewhere.

Now that we have an understanding of the input field let’s look at the result of our query “test”. Below we will find out that whatever we input is going to be placed into the html within bold tags. This will certainly cause a vulnerability without proper input validation as we can simply insert javascript into the html.

Below we see the javacsript for this challenge. Once again we see no input sanitation or validation. First we see that if there is no get request with the id ‘query’ to return the main page. Otherwise do a get request with the id ‘query’ and a value. We can then see the bold html tags. %lt; is the equivalent of the less than sign ‘<’ so we wind up with <b>query</b> inserted into the page as we can see with the self.render_string() function as it includes the message variable where this html is stored.

Being that this page is so small it is easy to look for input validation/sanitation on the client-side of things. However, client-side sanitation is not all that is necessary as hackers can typically find methods of disabling the sanitation. For instance, input validation with javascript can be avoided by simply disabling javascript entirely. In the end the server is what must be protected as it is what enables XSS vulnerabilities to be exploitable. Another easy way to bypass client-side filtering is by using something like PostMan to forge forms without validation to communicate with the server. Client-side validation is typically seen as a first layer of defense. Unfortunately and fortunately we are not able to see server-side input validation/sanitation so we will have to simply see what happens!

The most basic XSS payload of all time has to be <script>alert(1);</script> so let’s give this a shot. Tada, there’s your first XSS vulnerability exploited. We can see that it was inputted into the url and rather than printing it on the page in clear text it is executed as javascript.

Level 2: Persistence is key

We are going to experience a stored XSS vulnerability in this example. This is because the message we submit containing the payload on this site is going to be saved on the server and when a user connects to this page the server will send the message containing the payload. Stored XSS attacks are also known as a persistent attack.

First things first, the previous message is shown in the html within the <blockquote> tags. We see other tags within the message such as an italics and a color which indicates that our input is going to be translated into html. Below we can see the html code for this form and the initial message.

To test if our theory is correct about inserting html into our message we can attempt to send a message containing a tag such as bolding a word. To do this I will type in “testing <b>bold</b>” and select “Share Status!”. The result is exactly what we are looking for!

Below we can see the javascript that adds our input into the html. We see that the date is added and then the blockquote html tags along with the message. Lastly and most importantly, we see the line “containerEl.innerHTML += html;” which is going to add the html variable to the innerHTML of the post-container Element being that containerE1 is equal to document.getElementById(“post-container”). innerHTML is used to alter HTML elements without having to refresh the page. What innerHTML does not do, is execute text within <script> tags. However, it does execute whatever is inside nearly any other tag that will do the same thing! There is a long list of possibilities for this however, the most common method is with the img tag. You simply enter a non-existing image in the href and then tac on an “onerror” which will be executed when the fake image fails to load.

Entering the below text <img src=”noimage” onerror=”alert(‘cramhack’)”> will execute the alert(‘cramhack’) to finish this challenge.

Level 3: That sinking feeling…

As with most things you should sort of create a routine for yourself to solve a problem. First step, look around the page and think about what features could be vulnerable. For this level we seem to only have 3 buttons that give us 3 different images. Let’s dive a bit deeper. In the HTML there didn’t seem to be anything special. Mainly just shows the 3 tabs and sets chooseTab to a number based on which one was clicked on.

Let’s take a look at the chooseTab function in the javascript code below. We know that the variable num is the tab number the user selects. We can also see that this alters the url (Ex. frame#1, frame#2 and so on…) therefore, this has potential to be a vulnerability. This number is also used to set window.location.hash which makes this a very good possibility of being the vulnerability. Window.location will redirect a page and the additional .hash extension means we will redirect the page based on a fragment identifier. For instance on the first tab it is window.location.hash = 1 which will give us the page for that fragment identifier (the first photo).

Also within the script we can see that num is being directly inserted into the <img src=” that defines what photo to reveal on the page. For instance the first tab has a photo at src=’/static/level3/cloud1.jpg’ so therefore if we can alter the num variable we can alter this script and exploit the vulnerability.

We can insert our payload right into the url. We know that our input is going to be added to the current <img src= so we can abuse this element. By putting an invalid .jpg filename we can then use the ‘onerror’ function to create an alert when the image does not load. It’s important here not to forget about the single quotation marks. An example payload would be; onerror=’alert(1)’
However, there are cases where you may need to comment the remainder of the line and close out of the <img> tag as so.; onerror=’alert(1)’/>”;#

Level 4: Context matters

For this example we have a user input form which is used to set a timer based on a user defined number of seconds. This timer will alert the client when the number of seconds is up. First let’s take a look at the HTML. We confirm that it is a form and we see the id of the value we enter is “timer”. We also see that it is using a GET request so the timer requires a separate network request to function. We will be able to analyze this GET request in the network options in the developer tools.

Next we view the source for the challenge. Unfortunately, there doesn’t seem to be anything within these functions that would allow us to manipulate the HTML by inserting our payload. However, we do see a reference to the ‘timer.html’ page which is relevant to our timer form’s input. Let’s do some dynamic analysis with the networking tool.

In developer tools we want to go to the “Network” tab and then we will put in whatever value we’d like in the form and submit it. Of course we are going to want the GET request with the timer value as this is relevant to our form. If we take a look at the “Response” tab for this GET request we see the startTimer() function. We see that it takes in a parameter of seconds and we can see further down that startTimer() takes in whatever value we enter. There is no evidence of input validation or sanitation so this can certainly be the vulnerability. This is especially interesting as the startTimer() is being called with an onload= attribute and thus we can manipulate this to execute our own payload.

While exploiting this we will need to be very cautious as we are going to insert our payload inside the standard code. While we are doing this we can observe the Console tab in developer tools for errors that will help guide us to a successful payload. For instance when I try adding some special characters I received an error which will show me where in the HTML the error occurred. Luckily, this also confirms that there is no input validation or sanitation that we have discovered so far.

Thinking of this logically; we start with an open single quotation so we will insert a value and then we will want to finish the startTimer() function by closing the quotation and parentheses along with the semicolon by inputting something such as the following.

This will cause an error because there is now an additional ‘); that we did not use. However, the onload attribute can execute more than one function. What we will do for our payload is insert our alert() function but utilize the closing ‘); that is already present in the code. Therefore, our final payload will be something such as…

5′); alert(‘cramhack

Level 5: Breaking protocol

This challenge shows us a DOM Based XSS. What this means is that the attack payload is executed by altering the DOM “environment” in the browser. We are altering the DOM environment in a way that the client side script runs in an unexpected or unintended manner. In no way are we altering the elements of the page, we are simply abusing the client side script.

There is quite a bit of unnecessary information so I’m not going to include as much as the past challenges. The first page is exactly what it looks like, a page with a link to a sign up page, nothing more.

Let’s checkout the signup page. Here in the HTML we see that the form is actually useless. It does nothing so we can forget about this being vulnerable. Next we see that the “Next” link is referenced to “confirm” but, what is confirm? A reference is meant to be a url or something defined and being that “confirm” is not defined in the HTML we are going to have to look a bit. If you look at the html of the home page or the url of the signup page you will see “signup?next=confirm” which answers the question as to where this variable is defined.

Another common vulnerability on websites is a redirect attack. Using a reputable website to redirect users to a malicious website can be a great way for hackers to lure users to their payload. Simply by changing the url we can alter the “Next >>” button on the page to redirect users to wherever we would like.


All we are doing here is defining the <a href= attribute in the HTML. You can actually use this attribute to call javascript and execute functions. To do this we will alter the url to include javascript:alert(“cramhack”);

Level 6: Follow the *bunny*

Alright let’s see what we got. Firstly, in the HTML we simply see the text portrayed on the screen with the div id “log”. Let’s just jump right into the source on this one as there is nothing more to see in the page’s HTML. We will go through the source below and break it down so we can understand exactly what this page is doing.

Starting with the body of the HTML at the bottom we can see that the name of the file path isn’t actually in the div id “log”. This means it must get added so let’s look up at the javascript.

Function includeGadget() takes in a url as a parameter. A script HTML element is made with the variable name ‘scriptE1’. For once, there is some input sanitation in the conditional statement. If the url contains ‘https’ or ‘http’ then “log” will be set to cannot load the url however, this is case sensitive so capitalized letters will bypass this sanitation. If we get past the input sanitation then the variable ‘scriptE1’ is going to load the src at the url. Next, we will update ‘log’ id on the page to include the file path and include error checking.

Being that this function literally loads a script based on the url parameter this could certainly be a major vulnerability. Let’s find out where the parameter is defined and where this function gets called.

In the code we even get a nice comment explaining exactly what the function does. Basically, we are going to either get the value after the # in the url or (‘| |’) we are going to default to /static/gadget.js which means we now have a way to execute a payload.

What will our payload  be is the question now. We actually have multiple options here. The most obvious choice is to host our payload on a web server we own. This would basically just need to include alert(‘cramhack’) and be able to avoid the input sanitation by utilizing something like capitalized letters in https.

The other option which I will use as it requires no setup is to utilize the data URI scheme. This provides a way to include data on a website as if the data was an external resource. Read more about it on wiki as they explain it best while providing examples.

In the end our payload is #data:text/javascript;charset=utf-8,alert(‘cramhack’) and you can get much fancier with this if you’d like. All you you do is place this in the url where the hash is currently and your done.
***If you are copying and pasting these answers, the single quotation marks may not copy/paste properly. If it does not work simply backspace each of the single quotation marks and place in news ones manually.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Create a website or blog at

Up ↑

%d bloggers like this: