Tags: web sqli 

Rating: 0

SQLite Voting

function is_valid($str) {
$banword = [
// dangerous chars
// " % ' * + / < = > \ _ ` ~ -
// whitespace chars
// dangerous functions
'blob', 'load_extension', 'char', 'unicode',
'(in|sub)str', '[lr]trim', 'like', 'glob', 'match', 'regexp',
'in', 'limit', 'order', 'union', 'join'
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $str)) {
return false;
return true;

// request to SQLite db, I skipped is_valid($id)
$res = $pdo->query("UPDATE vote SET count = count + 1 WHERE id = ${id}");
if ($res === false) {
die(json_encode(['error' => 'An error occurred while updating database']));

We can see that we've got a heavily filtered error-based blind sql injection.

## Solution

First we got the length of the flag by unumerating through `$LENGTH$` in the following payload:


I will explain the payload bit-by-bit later, but the flag was 38 characters long.

Then, we double hexed the `flag` so we can be sure that it only produces digits

sqlite> select hex('0123456789ABCDEF');

We also know that the length of the produced number is exactly 152-digit long.

You cannot pass integers bigger than `9223372036854775807` because they will get cast into floating numbers, but you can concatenate them as they were string, e.g. `9223372036854775807||9223372036854775807` will produce `92233720368547758079223372036854775807`. Thanks to this property we now can iterate over all composited 152-digit long `$NUMBER$` and use the `max(A, B)` function which will return the bigger one.


We get the double hexed flag which is:


- `abs(-9223372036854775808)` will cause integer overflow and hence throw an error
- `0x8000000000000000` is hex-encoded `-9223372036854775808`
- `nullif(A,B)` will return `NULL` if `A` equals `B`, returns `A` otherwise
- `ifnull(A,0x8000000000000000)` will return `0x8000000000000000` if `A` is `NULL`, otherwise `A` is returned.
- `max(A,B)` returns lexicographically greater string.
- `hex(hex(flag)` "removes" all non-digit characters from flag