Rating:

Fortune Cookie - 2020 Defenit CTF (web, 507p, 15 solved)

Introduction

Fortune Cookie is a web task.

A full docker environment is given. It contains a node app container and a MongoDB database container.

The app allows clients to log in, write information to the database and read what informations they stored.

The client's username is stored in a signed cookie.

The flag can be obtained if the client can provide a number such that Math.floor(Math.random() * 0xdeaaaadbeef) === ${favoriteNumber} holds true.

Source code review

The login endpoint will allow any username, even if it is not a string. This username is then serialized and signed in a cookie.

app.post('/login', (req, res) => {
    let { username } = req.body;

    res.cookie('user', username, { signed: true });
    res.redirect('/');
});

The write endpoint allows a user to insert data in a collection. Both the author and content values can be objects, but there is no use to this.

The view endpoint shows a specific post. No vulnerability was identified on this endpoint.

The posts endpoint reads the user's posts. It uses the signed cookie to find every posts made by the current user. There is a NoSQL injection.

app.get('/posts', (req, res) => {

    let client = new MongoClient(MONGO_URL, { useNewUrlParser: true });
    let author = req.signedCookies.user;

    if (typeof author === 'string') {
        author = { author };
    }

    client.connect(function (err) {

        if (err) throw err;

        const db = client.db('fortuneCookie');
        const collection = db.collection('posts');

        collection
            .find(author)
            .toArray()
            .then((posts) => {
                res.render('posts', { posts })
            }
            );

        client.close();

    });

});

The flag is not in the database. Peeking at other users' payload won't be helpful.

The flag endpoint retrieves a parameter and check if Math.floor(Math.random() * 0xdeaaaadbeef) is equal to this parameter. The parameter is casted to int. This endpoint is not vulnerable.

Exploitation

As mentionned above, the posts endpoint is vulnerable to a NoSQL injection. It is possible to execute Javascript in the same process as the one used to check if the user's number is correct for the flag endpoint.

The random number generator used by MongoDB is XorShift128+. Attacks exist to retrieve its internal state. As interesting as this path would have been, this is not the solution.

Instead, it is possible to redefine the Math.floor function to return a constant. This can be done by login in with a username of username[$where]=(Math.floor=()=>0x586552),false. This will make it always return the number 5793106.

Two quick requests to /posts and to /flag?favoriteNumber=5793106 using this username will print the flag.

Flag: Defenit{c0n9r47ula7i0n5_0n_y0u2_9o0d_f02tun3_haHa}

Original writeup (https://github.com/TFNS/writeups/blob/master/2020-06-05-DefenitCTF/fortune_cookie/README.md).