There comes a time in every sysadmin/DBA/programmer’s life when they review their slow-query-log file and see something like this:

WHERE col.id = (select(0)from(select(sleep(90)))v)/*'+(select(0)from(select(sleep(90)))v)+'"+(select(0)from(select(sleep(90)))v)+"*/;

Usually this is about 10–15 minutes after they’ve started receiving reports that their website/product/API is down.

Because someone made it go to bed early: welcome to the terrifying world of SLEEP() .

What Does SLEEP() Do?

SLEEP(n) pauses the database for a given number of n seconds.

Not to be confused for the Sleep status in the MySQL console, which describes connection statuses, the SLEEP() function does one thing and does it very well: it keeps things from happening until its timer expires.

What good is it? What’s it for?

A few things! It’s good for purposeful delays (perhaps you’re waiting for locks to expire before doing something like backups?) and hacking.

Wait, “Hacking,” you said?

Yes, hacking.

Scanners Gonna Scan

Scanning websites for vulnerabilities is a trivial exercise in today’s time if the administration team hasn’t put safeties in place.

An excellent tool for security auditing (and hacking by extension) is sqlmap.

This program automatically identifies various endpoints on a website that might be using a MySQL connection underneath the hood and throws garbage, fuzzing data, and SLEEP commands at them like there’s no tomorrow.

The output from these scans often end up, especially in PHP/MySQL projects using the old mysql or mysqli connectors instead of PDO , enumerating a list of vulnerable AJAX commands or GET/POST results.

But how is SLEEP() involved?

By forcing the database to sleep on a public-facing website for a given number of seconds (each number being a unique roll of the dice) sqlmap and its cousins can hit dozens of endpoints at once and if/once the database freezes up count how long its out of action to figure out which of the many commands it tried actually worked.

A Quick Dissection

The example posted at the top of the article is a common enough one that it doesn’t represent a “new threat.” It still works though.

If you execute a SELECT statement where the conditional statement has been replaced with something like the following, then it will absolutely lock up the database:

WHERE col.id = (select(0)from(select(sleep(90)))v)/*'+(select(0)from(select(sleep(90)))v)+'"+(select(0)from(select(sleep(90)))v)+"*/;

Notice the clever use of the various operators and delimiters: these are designed to fool custom sanitization routines that programmers relied on rather than a proper PDO (I’m picking on PHP because its easily the the easiest web language to write unsafe MySQL integrations with).

I’ve even seen code that, while not PDO, used at least the mysqli connector instead of the old deprecated mysql but completely forgot to use mysqli_real_escape_string on incoming data from the client.

A Bad PHP Example

Something like this, for instance, is a common enough pattern it deserves recognition for just how easily it can ruin your project:

function lookupProductsByID($product_ids) {

global $conn;

$q = "SELECT * FROM product WHERE id IN (" . implode(',', $product_ids) .")";

$result = mysqli_query($q); ...

In the above example a programmer is using the implode() function to create a comma delimited list of IDs.

Note that NO validation was done on the input: that incoming array could be full of SLEEP commands or an instruction that ended up dumping the database table schemas to the browser window. It could inject data into a column used to render the UX, like a cross-site-script.

In this case it would’ve been infinitely better to at least iterate over the elements of the array and validate them. Were you expecting int values? Then use intval() and force each element to prove its int -eyness.

Or use PDO and bind the values which will auto-sanitize and prevent SQL injections.

So PDO Solves Everything?

No. PDO doesn’t solve everything.