October 23, 2026
PortSwigger’s XSS Labs: Complete Walkthrough
Disclaimer: This write-up is for educational purposes only. All attacks are done on PortSwigger’s Web Security Academy labs, which are made…

By Muhammad Ayan
17 min read
- 1 1. Reflected XSS into HTML context with nothing encoded
- 2 2. Stored XSS into HTML context with nothing encoded
- 3 3. DOM XSS in document.write sink using source location.search
- 4 4. DOM XSS in innerHTML sink using source location.search
- 5 5. DOM XSS in jQuery anchor href attribute sink using location.search source
Disclaimer: This write-up is for educational purposes only. All attacks are done on PortSwigger's Web Security Academy labs, which are made to be attacked legally. Do not use these techniques on any website or system unless you have clear, written permission from its owner.
Before doing labs first lets discuss what is XSS and how to prevent it.
What is Cross-Site Scripting (XSS)? XSS is a code injection vulnerability. In this type of vulnerability a hacker inserts malicious scripts of code like JavaScript into a trusted website. When a victim opens that website the browser executed that malicious code as thinking it is safe due to a trusted website.
Why is it dangerous? As the script runs in the user's browser an attacker can steal user's session cookies, hijack his accounts, read sensitive data and the worst case is that attacker can redirect users to malicious sites.
Three main types of XSS:
- Stored XSS: The malicious script of code created by attacker is saved permanently on the website's server and anyone who views that page becomes a victim.
- Reflected XSS: In this case the script is embedded in a malicious link. Whenever a user clicks that link, the server immediately reflects the script back to execute in user's browser.
- DOM-based XSS: This type of XSS is entirely existing on client side. Here the browser's JavaScript unsafely processes the untrusted input directly from the URL or webpage structure.
How to Prevent It? Just never trust user input. Developers must use input validation (rejecting bad data) and output encoding (converting characters like < and > into safe text) so the browser knows to display the input rather than execute it as code.
1. Reflected XSS into HTML context with nothing encoded
Access the lab and you can see a webpage.
You can see that what ever you type is shown on webpage. Like if you search blog it will show you results of blog if there are any blogs and will show your typed text on screen. Now, this website is not properly encoding the characters like <> and will add the searched query into document and browser will then process it like a code not like a text. You can see in screenshot below.
As, you can see it executed it like a code. Now, to solve the lab we can try to use the script for alert.
Type this script tag in search box to show a alert on screen.
<script>alert(1)</script><script>alert(1)</script>This alert will show and your lab will be solved.
Congratulations Lab is solved.
2. Stored XSS into HTML context with nothing encoded
This lab contains a stored cross-site scripting vulnerability in the comment functionality. To solve this lab, submit a comment that calls the alert function when the blog post is viewed.
Access the lab and you will see that there are many blog posts. Open any one of them. Scroll Down till Comment section. Now, here you can post a comment. And that comment will be listed here. But here we will first try to paste a h1 tag here to see whether that is shown as a simple text or is executed like html. Type sample data like this to post comment and add a h1 tag in comment.
Now, go back to that blog post and see that your tag is not shows as a text. It is executed as html code. You can see other comments are short and black and our comment is an html h1 tag.
So, it means we can also store our script here and it will processed like a script not like a text.
This time use this tag to solve lab and store this script. Also add email, name and website like previous.
<script>alert()</script><script>alert()</script>Lab is now solved. Now, if you will go back to blog you will see a alert message.
Now, you must have understood that stored xss is even more dangerous than reflected. As, this is stored and runs the script whenever a user will open this post. Now, think of what a hacker can do. He can make scripts to get your sessions and get privileges of a user who has now became a victim.
Congratulations Lab is solved.
3. DOM XSS in document.write sink using source location.search
Access the lab and search something on the blog. After it press Ctrl Shift I to open developer options on firefox. You will see DOM table.
You can see that your search term is inside an image tag. There is also a javascript tag above the img tag which is defining the whole vulnerability. Our searched keywords is shown at two places. One comes with response according to our request and the second is first written to DOM using the js document.write function. If we attack the response as part of the direct HTTP response, that will be reflected XSS. If we attack the way this javascript is manipulating thsi DOM, then it will be a DOM based XSS attack. In DOM our keyword in being desplayed as the source attribute of the image tag. So, now we will break that attribute and give the image tag a second attribute. You may have guessed. Yes we will use alert tag so when image is loaded alert will pop-up.
We will add a double quote to break out from that specific attribute and define a new attribute.
blackwolf"><svg onload=alert()>blackwolf"><svg onload=alert()>And here we have a alert and lab is solved.
Congratulations Lab is solved.
4. DOM XSS in innerHTML sink using source location.search
Like previous lab search something in search box and you can see it in dom after opening inspect in browser. Now, you might think to use same script tag in the search. So, let me tell you that the script tag will be injected but will not be executed because this is an innerHTML function not a document.write function and the script will not execute as this is part of browswer security. So, let us try to pass in image tag with a source that we know will error out and then attach the onerror attribute equals javascript alert. Type this in search box.
<img src='0' onerror='alert()'><img src='0' onerror='alert()'>And now you can see we got it executed and error showed up.
Congratulations Lab is solved.
5. DOM XSS in jQuery anchor href attribute sink using location.search source
Access the lab. We know that there is a vulnerable backlink so just need to find where is that backlink. Now, in our case it is the submit feedback section so click it.
On the submit feedback section you have two buttons. One to submit and another for back. Right click that back button and inspect it. Here, you can see it is a anchor tag having id of backlink. And this is taking us to home directory of the website.
Read the below script and read it. You will see that jQuery is being used here. After analyzing it we can now start solving it. Look at the address bar above. You can change / parameter with javascript.
Like this.
javascript:alert(document.cookie)javascript:alert(document.cookie)Write it like this in address bar. Press enter.
Lab is solved and if you click the back button it will execute this JavaScript in the anchor tag and will show a alert on screen.
Congratulations Lab is solved.
6. DOM XSS in jQuery selector sink using a hashchange event
This lab contains a DOM-based cross-site scripting vulnerability on the home page. It uses jQuery's $() selector function to auto-scroll to a given post, whose title is passed via the location.hash property. To solve the lab, deliver an exploit to the victim that calls the print() function in their browser.
Access this lab. Now, if you are thinking that these are little difficult. You just need to get some knowledge of JavaScript and you will be understanding it yourself.
After accessing the lab go read the page source of web page by Ctrl U. You have a hint that there is a jQuery vulnerability here. This is the jQuery script.
After reading the code we can say that what our JavaScript is actually doing. It's looking for h2 element that is containing the value following the hash in the URL. After getting that element it is then calling the scroll Into View. Now, hash means like if you have seen that when you want to go on same page at a specific section you will use it like e.g. #faqs at end of url.
Now, It's time to solve the lab. We will use an iframe. Open the exploit server by clicking Go to exploit server.
Write this in that place. Here replace the url of lab with your lab url.
<iframe src="https://YOUR-LAB-ID.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe><iframe src="https://YOUR-LAB-ID.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>Write this in the body section inside exploit server. After writing it click view exploit so you can see how it will be on victim side.
And here you can see that you are getting a print screen on clicking view exploit.
Now its time to click deliver exploit to victim. And your lab will be solved.
Congratulations Lab is solved.
7. Reflected XSS into attribute with angle brackets HTML-encoded
Access the lab. Search something unique in search box e.g. blackwolf. Inspect it to see that where it is inside code.
our search term in appearing inside the value attribute of that input element. Now we will try to breakout through this value attribute and create a new one. E.g. a mouseover event so we can pop up a js alert when that event occurs.
We will try to break this by using double quote. By this we will be breaking out form value attribute but still are in the input tag.
Now search this inside the search box.
blackwolf" onmouseover='alert()'blackwolf" onmouseover='alert()'Lab is solved. Now, try to hover over the search box and you will see an alert. This is dangerous right? Any way we should focus on secure coding to avoid these attacks.
Congratulations Lab is solved.
8. Stored XSS into anchor href attribute with double quotes HTML-encoded
Access the lab. We are seeing blogs and each blog give us option to post a comment. Go open a blog and head over to comment section. Put a fake comment by filling fake information.
Post this comment and go back to blog and you will see in comments that your comment name is a hyperlink. Means it can be clicked. And it is taking you to the site that you entered on clicking it.
Inspect the wolf tag and see it is a anchor tag with href attribute.
Now, we will try to execute a JavaScript inside this href attribute. Now, post the comment with a new comment and website is the section which is inside the href attribute. Write like this.
In website section write like this.
javascript:alert()javascript:alert()Actually the real issue is here that the developer of this website is allowing us to inject arbitary links to this page. So, that is the insecure part here. Now post the comment and go back to the blog and to the comment section. Click on your comment hyperlink and you will see this alert. Lab is now solved. Now, think of it like if on a website a xss attack is stored in a hyperlink comment and if someone open that website and click accidently on it so the JavaScript will execute. Developers should pay attention on this.
Congratulations Lab is solved.
9. Reflected XSS into a JavaScript string with angle brackets HTML encoded
Access the lab. Here we can't use h1 tag like with angular brackets as those are encoded by developer of website. Search something in the search box like your name and you will see it in inspect tag. You will see your name inside a JavaScript.
Now search this inside search box to get out of the string from JavaScript.
' + alert() + '' + alert() + 'And you got an alert.
Congratulations Lab is solved.
10. DOM XSS in document.write sink using source location.search inside a select element
Access the lab. You will see a shop. Open any product. Go down and inspect the check stock button. You can see a script and a select tag.
You will be seeing this url on address bar.
Next step is to add a store id parameter to the url query string.
Write this next to productID =1
&storeId=blackwolf</select><img src="1" onerror="alert()">&storeId=blackwolf</select><img src="1" onerror="alert()">Like this.
And lab is solved as you can see alert.
Congratulations Lab is solved.
11. DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded
Access the lab. Now, to check whether our input is handled correctly or not search this in search box.
{{ 1 + 1}}{{ 1 + 1}}You will see 2 on screen instead of this query shown so it means it is being executed as a code instead of being a text. It means vulnerability exists here.
Use this payload in search box to solve the lab now.
{{$on.constructor('alert()')()}}{{$on.constructor('alert()')()}}Now, you will get an alert and lab is solved.
Congratulations Lab is solved.
12. Reflected DOM XSS
Access the lab. In this lab the JavaScript is requested from another file. You an see that inside the network tab or in inspect section. Analyze that code. After analyzing the code we can Search this in search box to solve lab.
\"-alert()}//\"-alert()}//We got an alert.
Congratulations Lab is solved.
13. Stored DOM XSS
This is a stored DOM based XSS. Access the lab. Open any blog post. And open the network tab in inspect and you will see a JavaScript file named CommentsWithVulnerableEscapeHtml.js.
Open that JavaScript file by clicking on it. After reading the code we got a clue so now try to post a comment like this.
After posting go back to blog. You will see that like lab was also telling that due to some reason closing tag html is not encoded.
Now we will try to generate an exploit. Try this in this way.
<h1><h1> Hello guys </h1><h1><h1> Hello guys </h1>
Post this comment and go back to blog. And you can see first h1 tag was encoded but second was executed as a h1 html tag.
So, now we can execute real payload. Write this in comment and fill other places with fake data.
<h1><h1 onmouseover="alert()">Hi bro lab is solved</h1><h1><h1 onmouseover="alert()">Hi bro lab is solved</h1>Now hover over your comment and you will see an alert.
But to solve the lab you have to make it like that if anyone just come on this page than code shouf be executed not only on hover so use this. Write this in comment section.
<h1><img src="0" onerror="alert()"><h1><img src="0" onerror="alert()">Now, you will get an alert on just going on to that blog page.
Congratulations Lab is solved.
14. Reflected XSS into HTML context with most tags and attributes blocked
Access this lab and this one uses a WAF. Use burp intruder or any tool you like for this. Try to use any above payloads here like img one and you will see that Tag is not allowed. It means firewall is blocking us. But it does not mean that every payload is being blocked liek if you write <> it is not being detected by firewall also this is also not giving any error of firewall.
Actually we knew this by using following paylaod and sending it to intruder. Means first search it in search box then capture request form burp and send to intruder.
<img src="0" onerror="alert()"><img src="0" onerror="alert()">In Intruder go and first click clear all paylaods.
Write two angle brackets like this and add a payload inside them by clicking inside the brackets and click add.
Now to do a brute force attack for paylods go to cheat sheet on portswigger and copy tags to clipboard to copy all payloads.
Paste them here.
Click start attack.
You will see that the 200 status code are those which are not being stopped by WAF and body tag is in them. Now if you try this paylaod you will still get Web Application Firewall error means we can use body but we cannot use onload attribute with it. We can again use intruder to find that.
Write body%20 inside brackets and add a payload here like this.
This time Copy events to clipboard. Paste them in intruder remember to remove the previous one by clear.
Start attack after it and then you will see those that are being allowed from WAF. We have found some option but all those require user interaction like making page size change and we don't want to user to interact then to execute our payload. So now go inside exploit server in the lab.
Click here on Go to exploit server:
Now paste the following iframe inside Body of exploit server but change the url of web security with your challenge url.
<iframe src="https://0ace007b04487a2080806c5600b400cd.web-security-academy.net/?search=%22%3E%3Cbody%20onresize=print()%3E" onload=this.style.width='100px'><iframe src="https://0ace007b04487a2080806c5600b400cd.web-security-academy.net/?search=%22%3E%3Cbody%20onresize=print()%3E" onload=this.style.width='100px'>Now, click deliver exploit to victim.
And you are done.
Congratulations Lab is solved.
15. Reflected XSS into HTML context with all tags blocked except custom ones
Access the lab. Here all standard tags like h1 or script tags are blocked so we need to try custom tags. Custom tags are often used with hyphens. Like and you can see that this one did not gave any firewall warning.
Write this inside search box. Press enter.
<custom-tag onfocus='alert(document.cookie)'id='x'tabindex="1"><custom-tag onfocus='alert(document.cookie)'id='x'tabindex="1">Now, to execute it just again load that page but before that write #x at end of the url and press enter and you will see an alert.
Now, to solve the lab you have to run this on victim browser so go to exploit server and paste this there.
<script>
location = 'https://0a3b00280476130b81e0b6a2004a00fb.web-security-academy.net/?search=%3Cxss+id%3Dx+onfocus%3Dalert%28document.cookie%29%20tabindex=1%3E#x';
</script><script>
location = 'https://0a3b00280476130b81e0b6a2004a00fb.web-security-academy.net/?search=%3Cxss+id%3Dx+onfocus%3Dalert%28document.cookie%29%20tabindex=1%3E#x';
</script>Write this in body section and Deliver exploit to victim.
Finally you are done from this lab.
Congratulations Lab is solved.
16. Reflected XSS with some SVG markup allowed
Access this lab. Try this payload to practise.
<img src=1 onerror=alert(1)><img src=1 onerror=alert(1)>You will get an error from firewall. So, now again we have to try the intruder to bruteforcer different tags to see which one woks agianst this firewall.
So, send the capture request from the search to intruder.
Copy tags to clipboard from cheatsheet of portswigger.
Set the intruder like this same like you did for a previous one. Start attack and you will see the one's with 200 status code so you can also see svg and animatetransform that we will use here. This time use it like this. And =1 should be added inside the payload by add button.
GET /?search=<svg><animateTransform%20=1>HTTP/1.1GET /?search=<svg><animateTransform%20=1>HTTP/1.1For this copy events to clipboard.
Paste these in payloads and then start attack. Now, you will see that onbegin is giving 200 status code.
Now enter this in select statement and press enter.
<svg><animateTransform onbegin='alert(1)'><svg><animateTransform onbegin='alert(1)'>Alert came and lab is solved.
Congratulations Lab is solved.
17. Reflected XSS in canonical link tag
Access the lab. Here there is no search box so one way to inject is by url. For that we can create a query string at the end of url. Write this at the end of url.
?blackwolf?blackwolfAnd you can find it inside inspect inside an href equal to canonical. Also you can check this to confirm that you can inject.
?'onclick='alert(1)?'onclick='alert(1)Now, we want to know how to click somehow on this but there is nothing related to it loading on page so we can use access keys for that. Access key is like a shortcut to click a button without mouse and just by pressing combination of keys.
?'accesskey='x'onclick='alert(1)?'accesskey='x'onclick='alert(1)And this will work. Means when someone will use those key combination on browser that support them then it will be executed and alert will show. Shortcut can be like.
ALT+SHIFT+X
CTRL+ALT+X
Alt+X
Congratulations Lab is solved.
18. Reflected XSS into a JavaScript string with single quote and backslash escaped
Access the lab. The lab is self explaning what will not work. As single quote and \ are being escaped from the input. Now, try to excape from script tag by this. Write this in search box and press enter.
</script><script>alert()</script></script><script>alert()</script>You will get alert and lab is solved.
Congratulations Lab is solved.
19. Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
Access the lab and do what we did till yet liek searching your name and seeing where is that string showed in inspect tag also read the JavaScript.
As lab is encoding and escaping different things then this is best payload after multiple tries of different. Double // with comment out the rest of the javascript.
Search this as input in search box and press enter.
\'-alert(1)//\'-alert(1)//We got an alert and lab is solved.
Congratulations Lab is solved.
20. Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
Access this lab and you can see that this one is little more protected like more excaped and encoded things. Here you can comment on blog posts. Post a comment like we did in previous labs and you will notice on coming back to blog that the comment has a hyperlink on it like one of our previous lab.
So, we will try to use payloads in our website section in comment posting. After trying different payloads and seeing that our characters are being escaped we can use this one.
Fill the full form in comment section and in website box write this.
http://foo?'-alert(1)-'http://foo?'-alert(1)-'Post this comment.
Congratulations Lab is solved.
21. Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
Access this lab. This one has even more security by escaping different characters.
Follow all the steps in prevous labs to search your name and finding it in inspect tab in dev tools and read teh javascript code to understand its mechanism.
Note: if you find these things difficult liek the javascript or html tags you can further read them from official documentation. https://developer.mozilla.org/en-US/
Use this paylaod in search box and press enter.
${alert(1)}${alert(1)}
You got alert and solved the lab.
Congratulations Lab is solved.
Congratulations! You made it to the end, If you have any feedback or questions, drop them in the comments below. Follow me and stay connected. Happy Hunting!