---
# System prepended metadata

title: PortSwigger - Reflected XSS
tags: [XSS, Web]

---

[Toc]
# What is reflected cross-site scripting?
:::info
:bulb: Reflected cross-site scripting (or XSS) arises when an application receives data in an HTTP request and includes that data within the immediate response in an unsafe way.
:::

# XSS between HTML tags

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

We got a website has a search function which receives the user-supplied search term in a URL parameter:

![](https://hackmd.io/_uploads/Bk6K4XCs3.png)

Then if our input is:
```htmlmixed=
</h1><script>alert('hacked!!');</script><h1>
```
The web app will add it in the HTML like this:
```htmlmixed=
    <h1>0 search results for '</h1>
    <script>alert('hacked!!');</script>
    <h1>'</h1>
```
And the browser is simply rendering the HTML and executing any valid script embedded in it
![](https://hackmd.io/_uploads/S19lPQ0oh.png)

:::success
**Solved** :thumbsup: 
:::

![](https://hackmd.io/_uploads/S1hLw7Ai2.png)


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


In this lab, we have been blocked from using some tags like this

![](https://hackmd.io/_uploads/rkjHbwmn3.png)

I will use Burp Intruder to check if there are any unblocked tag that we can use. Send request to Intruder and insert a new payload marker like this

![](https://hackmd.io/_uploads/BJ-0fwm23.png)

Next, we need to copy all `tag` from XSS cheat sheet then paste it in the Payload setting in the intruder's payload tab

![](https://hackmd.io/_uploads/rkjENPXh2.png)

Click Start attack, then check the response. 

![](https://hackmd.io/_uploads/SkEeHD7h2.png)

=> 2 tags are not blocked are `body` and `custom tag`

This lab also blocked some attribute, so we will need to check that too

![](https://hackmd.io/_uploads/Hkt9wPXh2.png)

=> There are 5 attributes we can use

- **`onbeforeinput`**: is tringgered just before the user's input is processed by the browser
- **`onbeforetoggle`**: fires on a popover element (i.e. one that has a valid popover attribute) just before it is shown or hidden.
- **`onratechange`**: is triggered when the playback rate of an audio or video element changes. It occurs when the user modifies the playback speed of the media
- **`onresize`**: is tringgered when the browser window or an element is resized
- **`onscrollend`**: is triggered when the user srolls the content of an element

We can use tag `body` and event attribute `onbeforeinput` to check if this web vulnerable to XSS 

```htmlmixed=
<body onbeforeinput=alert(1)></body>
```
And when we try to enter any charater in the input box, alert message will appear

![](https://hackmd.io/_uploads/rkMPCOm23.png)

A note from this lab:
![](https://hackmd.io/_uploads/Sk23jwX3n.png)

But all tags and attributes that we can use will need some trigger from the user, so we need to find another way :thinking_face: 

There is also an exploit server to send an exploit to the victim

![](https://hackmd.io/_uploads/r1QM2vm22.png)

The idea now is when victim send a get request to exploit server, we will send back to them an HTML page with embedded blog page has XSS vulnerable, along with executable payload
```htmlmixed=
<iframe src="https://0a54004603838f07841a4017000c00c0.web-security-academy.net/?search=<body onresize=alert(1)></body>" onload=this.style.width='100px' >
</iframe>
```

This code will do 2 things:
1. Load the embedded blog with injected XSS payload
2. Use `onload` event to reload the page and style it with new different width. This will trigger XSS payload we have injected before

Add code to Body in Exploit server

![](https://hackmd.io/_uploads/HkzyVYX3n.png)

Click View Exploit to see how it works, if we are the victim

![](https://hackmd.io/_uploads/ryyQNY7h2.png)

Finally, click Store and click Delivery to the vitim, to send our exploit

Hmmm, still not solve :thinking_face: 

This lab want me to call `print()` function so I will change it a little bit

```htmlmixed=
<iframe src="https://0a54004603838f07841a4017000c00c0.web-security-academy.net/?search=<body onresize=print()></body>" onload=this.style.width='100px' >
</iframe>
```
Now send it

![](https://hackmd.io/_uploads/B1KUSFmh2.png)

:::success
**Solved** :thumbsup: 
:::

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


I will use the same method in the previous lab to check which tags and attributes are allowed.

**Allow tags:**

![](https://hackmd.io/_uploads/HkA3mSvnn.png)

**Allow attributes**

![](https://hackmd.io/_uploads/SyGTNHD3n.png)

Seems like it not blocked any attributes :smiley: 

So I try using the same payload in the previous lab to test xss vulnerable.

```htmlmixed=
<x onbeforeinput=alert(1)></x>
```

But it's not working :thinking_face:. Then I figured out

:::info
**`<body onbeforeinput>` :** When the `onbeforeinput` event is attached to the `<body>` element, *it captures input events that occur anywhere within the document body*. This includes input events within any child elements or custom tags contained within the `<body>`
    
**`<custom-tag onbeforeinput>` :** When the `onbeforeinput` event is attached to a custom tag, *it captures input events specific to that custom element only*. The event will not propagate to parent elements like the `<body>`
:::
    
So let's try another way. We can try using `onfocus` event by using it like this:

```htmlmixed=
<x tabindex=1 onfocus=alert(1)></x>
```

**`focus`** refers to the state of an element when it is actively selected or receiving user input. When an element is focused, it typically means that it is ready to accept user input, such as keyboard events or mouse clicks
    
**`onfocus`** is triggered when an element receives focus
    
**`tabindex`** attribute specifies the tab order of an element (when "tab" button is used for navigating)
    
Example:

![](https://hackmd.io/_uploads/SyyMfID33.png)

The element that gets focus when the tab button is pressed will be W3Schools, Microsoft, Google respectively
    
So with above payload, we will get alert when the tab button is pressed
    
![](https://hackmd.io/_uploads/SyoHX8P22.png)

To avoid having to wait for the user to interact (waiting for the user to press the tab button) we can use the `autofocus` attribute

**`autofocus:`** If an element on the page has the `autofocus` attribute, it will receive focus automatically when the page loads. Only one element on the page can have the `autofocus` attribute, and it takes precedence over other factors
    
So with this payload
    
```htmlmixed=
<x autofocus tabindex=1 onfocus=alert(1)></x>
```

The alert pops up right after the page is loaded

![](https://hackmd.io/_uploads/Hke-PID32.png)

Let's delivery payload to the user

First I try using `<iframe>` like previous lab, but it not working. Looks like the site doesn't allow us to embed it

![](https://hackmd.io/_uploads/Sk8JKUvhn.png)

Then I try using `<script> location`
```htmlmixed=
<script>
    location="https://0a9c00d6039f7b3d8305419700e80021.web-security-academy.net/?search=%3Cx+autofocus+tabindex%3D1+onfocus%3Dalert%28document.cookie%29%3E%3C%2Fx%3E"
</script>
```

![](https://hackmd.io/_uploads/r1JU9Lwhn.png)

Now store it, and delivery it to the victim

![](https://hackmd.io/_uploads/Symrc8P22.png)

:::success
**Solved** :thumbsup: 
:::

### EXPERT Lab: Reflected XSS with event handlers and href attributes blocked
When testing for reflected and stored XSS, a key task is to identify the XSS context:
- The location within the response where attacker-controllable data appears
- Any input validation or other processing that is being performed on that data by the application
#### The location:

![](https://hackmd.io/_uploads/ryDtpkq2h.png)

#### Input validation:
Check what tag we can use:

![](https://hackmd.io/_uploads/ry1M7x53n.png)

Check what event we can use:

![](https://hackmd.io/_uploads/BJ3GLg9hh.png)

Check if we can use `href` attribute:

![](https://hackmd.io/_uploads/B1c_Bb52h.png)

Just like the description of this lab, this lab contains a reflected XSS vulnerability with some whitelisted tags, but all events and anchor `href` attributes are blocked

So I wonder if anchor `href` attributes are not blocked, what will the payload look like to be able to call the alert function

And I found an interesting way :smiley: 

If you want to use the `href` attribute to trigger an alert dialog, achieve that by setting the href value to **`javascript:alert('Hello!');`**

Here's an example:

```htmlmixed=
<a href="javascript:alert('Hello!');">Click Me</a>
```
And if you don't know just like me, that when you type **`javascript:alert('hello');`** in your browser's search bar when browsing a web page.

Boom :boom: You get an alert box :smiley: 

![](https://hackmd.io/_uploads/Hk2zBM523.png)

Back to our lab, cause we can't use anchor `href` let check out others element that we can use

Then I research about how `<svg>`, `<animate>` element work

**`<svg>`** is an HTML tag used to embed SVG content in a web page. SVG is a markup language for describing two-dimentsional vector graphics that can be rendered in web browser

Here is an example:

```htmlmixed=
<!DOCTYPE html>
<html>
<body>

<h1>The svg element</h1>

<svg height="130" width="500">
  <rect x="50" y="50" width="100" height="100" fill="blue">
  </rect>
</svg>

</body>
</html>
```

![](https://hackmd.io/_uploads/S1G00zqh3.png)

 

The above code uses the `<rect>` element to draw a rectangle with the attributes defined inside the element.

The SVG **`<animate>`** element provides a way to animate an attribute of an element over time

Here is an example:

```htmlembedded=
<!DOCTYPE html>
<html>
<body>

<h1>The svg element</h1>

<svg height="130" width="500">
  <rect x="50" y="50" width="100" height="100" fill="blue">
  <animate 
           attributeName="fill" 
           dur="2s" 
           repeatCount="indefinite"
           values="blue;red;green;yellow;blue" />
  </rect>
</svg>

</body>
</html>
```

![](https://hackmd.io/_uploads/B1GP5Mqnh.png)

The `<animate>` tag is used to animate the `fill` attribute of the rectangle. The `attributeName` is se to `fill`, indicating that the fill color will be animated.

The values attribute contains a list of colors separated by semicolons (;). Each color represents the fill color at a specific point in time during the animation

P/s: You can try to run the code to understand how it works

If you noticed, we just discovered a new way to use `href` instead of `<a href=` which is blocked :sunglasses: 

Based on the two examples above,we can see that if we don't want to write it like this:

```htmlmixed=
fill="blue"
```

We can write it like this:

```htmlmixed=
<animate 
         attributeName="fill" 
         values="blue" />
```

![](https://hackmd.io/_uploads/SyrwEQqnh.png)

Soooo :smirk: 

If we can't use this

```htmlmixed=
<a href="javascript:alert('Hello!');"
```

We can use like this =)) 

```htmlmixed=
<svg>
    <a>
        <animate
            attributeName="href"
            values="javascript:alert('Hello!');" />
    </a>
</svg>
```

And if we want to display text in SVG we can use **`<text>`** like this:

```htmlmixed=
<svg>
    <a>
        <animate
            attributeName="href"
            values="javascript:alert('Hello!');" />
        <text x=20 y=20>
        Click me
        </text>
    </a>
    
</svg>
```

![](https://hackmd.io/_uploads/BJFyv7chh.png)

:::success
**Solved** :thumbsup: 
:::

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

#### The location: 

![](https://hackmd.io/_uploads/BJsoESc33.png)

#### The validation:

Check what tag we can use:

![](https://hackmd.io/_uploads/SJ7orrc23.png)

Check what event we can use:

![](https://hackmd.io/_uploads/rkDfDrch3.png)

There are 4 tags we can use, I try using somepayload from this cheatsheet.

![](https://hackmd.io/_uploads/rym-OSqhn.png)

And it works :smiley: 

![](https://hackmd.io/_uploads/BJrBdrq22.png)

Explain payload:
```htmlmixed=
<svg><animatetransform onbegin=alert(1) attributeName=transform>
```
**`<animatetransform>`** element animates a transformation attribute on its target element, thereby allowing animations to control translation, scaling, rotation, and/or skewing.

**`<onbegin>`** attribute is an event handler attribute that can be used with animation elements in SVG. It specifies a JavaScript function to be executed when the animation begins.

# XSS in HTML tag attributes

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

This lab prevented XSS attacks by using HTML-encoded angle brackets

![](https://hackmd.io/_uploads/rkFRZpnh3.png)

But there is another place that reflects the input we enter

![](https://hackmd.io/_uploads/Sk3TXa3h3.png)

So I tried to break out of this by using double quote `"` and inject some attribute like this

```htmlmixed=
abc" autofocus onfocus=alert(1)
```

And it really is reflected into the response

![](https://hackmd.io/_uploads/HJWLSa223.png)

![](https://hackmd.io/_uploads/HJb5HT23n.png)

:::success
**Solved** :thumbsup: 
:::

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

**`canonical`** tag in HTML is a meta tag used to indicate the preferred or canonical version of a web page. It helps address the issue of duplicate content on the internet, where multiple URLs may lead to the same or similar content.
:::spoiler
Here's an example to illustrate how the canonical tag helps address the issue of duplicate content:

Let's say you have an e-commerce website selling a product, and you have multiple URLs that can access the same product page. For instance:

1. `https://www.example.com/products/product1`
2. `https://www.example.com/products/product1?source=homepage`
3. `https://www.example.com/products/product1?source=category`

Even though these URLs lead to the same product page, search engines might see them as separate pages with duplicate content. This can potentially dilute the ranking signals and make it harder for search engines to determine the preferred URL to display in search results

To address this, you would add a canonical tag to the `<head>` section of each page, indicating the preferred URL:

```htmlmixed=
<!-- Canonical tag on Page 1 -->
<link rel="canonical" href="https://www.example.com/products/product1" />

<!-- Canonical tag on Page 2 -->
<link rel="canonical" href="https://www.example.com/products/product1" />

<!-- Canonical tag on Page 3 -->
<link rel="canonical" href="https://www.example.com/products/product1" />
```
By specifying the same cononical URL on all pages, you're indicating to search engines that the preferred version of the content is `https://www.example.com/products/product1`. Search engines will then consolidate their indesing signals, such as backlinks and page authority, towards this URL. This help prevent duplicate content issues and ensures that search engines display the preferred URL in search results
:::

Sometimes, you might encounter websites that encode angle brakets but still allow you to inject attributes. Sometimes, these injections are possible even within tags that don't usually fire events automatically, such as a cononical tag. You can exploit this behavior using access keys and user interaction on Chrome. 

Access keys allow you to provide keyboard shortcuts that reference a specific element. The **`accesskey`** attribute allows you to define a letter that, when pressed in combination with other keys (these vary across different platforms), will cause events to fire

In this lab, we can see it using `canonical` tag

![](https://hackmd.io/_uploads/rJly1GzT3.png)

So I tried to send some random parameter to see if the canonical include my query parameter

![](https://hackmd.io/_uploads/r1BrkzM63.png)

Then I tried to break out of it by sending a single quote

![](https://hackmd.io/_uploads/BkWckfz6n.png)

We can see that this time my param `abc` is no longer in the string, but has become an attribute.

![](https://hackmd.io/_uploads/ryH9bMMp3.png)

Based on the description of the lab, we will need to inject an `accesskey` with value `x` and inject an event `onclick=alert(1)` to handle what happend if element is selected when user press the key combination

![](https://hackmd.io/_uploads/S1xd-GM6h.png)

:::success
**Solved** :+1: 
:::
# XSS into JavaScript

## Terminating the existing script

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

In this lab, our input have been encoded

![](https://hackmd.io/_uploads/H114Oi6p3.png)

But there are another place reflect our input

![](https://hackmd.io/_uploads/S14O_oT6h.png)

We can see that our input are in the `<script>` tag. In this simplest case, it is possible to simply close the script tag that is enclosing the existing JavaScript, and introduce some new HTML tags that will trigger execution of JavaScript. 

Payload:
```htmlmixed=
</script><script>alert(1)</script>
```

![](https://hackmd.io/_uploads/SkCkcsp6h.png)

Bingo :smirk: 

Browser receives our input as html tags :3 

![](https://hackmd.io/_uploads/Bk-pcoap2.png)

![](https://hackmd.io/_uploads/rJLkji6Th.png)

:::success
**Solved** :+1: 
:::

## Breaking out of a JavaScript string

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

Like previous lab, I found 2 place reflect our input. But both have been encoded.

![](https://hackmd.io/_uploads/S123knap3.png)

When we tried to input a single quote, we noticed that our browser received it as a close string symbol

![](https://hackmd.io/_uploads/SkePWhap2.png)

So we can input a close string symbol, `;` to end the current statement, `alert(1);` - what we want to execute and `//` to comment other things on that line

Payload:
```htmlmixed=
';alert(1);//
```

![](https://hackmd.io/_uploads/HJ1ZXnTan.png)

Bingo ;3

Here is how browser receives it

![](https://hackmd.io/_uploads/S1nuX366n.png)

![](https://hackmd.io/_uploads/HkKq73p63.png)

Another way to inject what we want to execute and still correct the current syntax of JavaScript is using operator like this

![](https://hackmd.io/_uploads/SJwHDn6ph.png)

![](https://hackmd.io/_uploads/rJe2Phaah.png)


:::success
**Solved** :+1: 
:::

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

:::info
:bulb: **Escape characters** are characters that can be interpreted in some alternate way then what we intended to. In JavaScript, to print these characers as it is, include backslash `\` in front of them.
:::

Example:

Because strings must be written within quotes, JavaScript will misunderstand this string

```javascript=
let text = "We are the so-called "Vikings" from the north.";
```
The string will be chopped to `"We are the so-called"` and we will get an error like this

![](https://hackmd.io/_uploads/HJgu_UVCh.png)

The solution to avoid this problem, is to use the backslash escape character

The blackslash `\` escape charaters turns special characters into string characters

| Code | Result | Description  |
| ---- | ------ | ------------ |
| `\'`   | `'`    | Single quote |
| `\"`   | `"`    | Double quote |
| `\\`   | `\`    | Backslash    |

The sequence `\"` inserts a double quote in a string:

![](https://hackmd.io/_uploads/HyEoxDE03.png)

In this lab, our input has been HTML-encoded

![](https://hackmd.io/_uploads/ByvzzDVRn.png)

Single quote has been escaped

![](https://hackmd.io/_uploads/H1i_MPNAn.png)

Backslash has been escaped in HTML tag but not in the JavaScript :smirk: 

![](https://hackmd.io/_uploads/ryWuXv4A3.png)

So what if our input is `\'` ...

![](https://hackmd.io/_uploads/BJFK4PV02.png)

Now browser received it as a closed string symbol :sunglasses: 

**Explain:**

![](https://hackmd.io/_uploads/SyjxPv4A2.png)

Server only add a backslash before single quote `'` so if we input `\'`, it will reflect as `\\'`. And we know that `\\` will be understand an escape character, represents as a backslash character, so the single quote `'` will be understand as a closed string symbol

Cause can closed the string now, we can easily, make the script to execute what we want

Payload:
```javascript=
\';alert(1);//
```

![](https://hackmd.io/_uploads/HyVxCPVRh.png)


![](https://hackmd.io/_uploads/rkum3w4C2.png)
:::success
**Solved** :+1: 
:::

