Etusloba

Description

problem description

Solution

First let's download the source code and take a look at it:

const fs = require("fs");
const path = require("path");
const express = require("express");

const server = express();

server.get("/", function (req, res) {
    res.end("<html><body>etulosba</body></html>");
});

server.get("/files/images/:name", function (req, res) {
    if (req.params.name.indexOf(".") === -1) {
        return res.status(400).json({ error: "invalid file name" });
    }

    res.sendFile(__dirname + path.join("/files/images/", req.params.name));
});

server.get("/files/binary/:name", function (req, res) {
    if (req.params.name.indexOf(".") !== -1) {
        return res.status(400).json({ error: "invalid file name" });
    }

    res.sendFile(path.resolve(__dirname, "/files/binary/", req.params.name));
});

fs.writeFileSync(path.join(__dirname, "flag.name"), process.env.FLAG_NAME);
fs.writeFileSync(path.join("/tmp", process.env.FLAG_NAME), process.env.FLAG);

server.listen(process.env.HTTP_PORT);

We see that the flag name is saved in a file called flag.name in the current directory, and the flag itself is saved on the /tmp directory of the machine, under a file with the same name as the flag name.

So we first need to figure out what is the flag name, and then try to access this file in the /tmp directory.

By analyzing the code above, we see that we have three paths where we can access the website, and the root path's not interesting for us.

When accessing the /files/images/ path, the following function will handle the request:

server.get("/files/images/:name", function (req, res) {
    if (req.params.name.indexOf(".") === -1) {
        return res.status(400).json({ error: "invalid file name" });
    }

    res.sendFile(__dirname + path.join("/files/images/", req.params.name));
});

The function will check if the file we are trying to access contains a ".". If it does, the server will call the sendFile function with with the path we provided and appending it to the web server's root path. So we will use it to get our flag.name file, since it is under the web server's root directory.

Let's try a simple path traversal: https://etulosba.chal.intentsummit.org/files/images/..%2f..%2fflag.name

And indeed, we get the flag.name file. Let's check it contents:

flag.name

So the next step is to access the /tmp/imaflagimaflag somehow. Since the tmp directory is on the root directory on the machine, we cannot use the function above (since it always send the file starting from the web server's root directory).

Let's analyze the third function:

server.get("/files/binary/:name", function (req, res) {
    if (req.params.name.indexOf(".") !== -1) {
        return res.status(400).json({ error: "invalid file name" });
    }

    res.sendFile(path.resolve(__dirname, "/files/binary/", req.params.name));
});

So here our path cannot have any "." characters. The function first calls path.resolve with the web server's root directory, and then appends /files/binary and the folder we are looking for.

So, how can we access the /tmp directory?

By looking at the documentation, we see the following example:

documentation

It means that if we manage to send to the function the paramter of req.params.name as "/tmp/imaflagimaflag", the function will return this exact path (without appending to the web server's root directory), and the server will send it back to us. Here is the payload:

https://etulosba.chal.intentsummit.org/files/binary/%2ftmp%2fimaflagimaflag

And we get the flag :)

Flag: INTENT{absolutely_great_job_with_the_absolute_path}