Server-side security war games: Part 15

We’re nearly at the end! This is the 2nd-last level.

We know there is a users table, with columns “username” and “password”. This time, the code just checks that the username exists. There’s no way to print out the data we want. Instead, we’ll have to do something cleverer.

First, we can deduce that the password we want belongs to the natas16 user. Check your assumption – does that user exist? Yes. Good.

We can still guess parts of the data we need. We’ll first guess the length of the password (it is probably 32 chars, like the others, but let’s make sure). Then, we can guess one character at at time.

To guess the length, inject natas16" AND LENGTH(password) = %d #

We already know the first part of that AND is true, so now the second part is all we care about. When it is false, the whole thing is false, and the webpage will say “This user doesn’t exist”. When it is true, the whole thing is true, and a single database row will be returned. The webpage will then say “This users exists”

Increment %d until true. (Yes the password is 32 chars).

Now, do the same thing for one-character slices of the password:

natas16" AND STRCMP(
    SUBSTR(password, %d, 1),
) = 0 #

There’s an error here that will cause your discovered password to contain only upper-case letters, or only lower-case letters (depending on how you do your search). SUBSTR is case-insensitive, so to coerce it into case-sensitivity, use BINARY on the operands:

natas16" AND STRCMP(
    BINARY(SUBSTR(password, %d, 1)),
) = 0 #

As with the length, increment %s along the alphabet (a..z, A..z, 0..9) until it returns true, then increment %d and do it again.

You’ll want to script that. I tried using sqlmap, an open-source tool for exploiting SQL injection, but I couldn’t get it to work. It kept getting HTTP 401 inexplicably – let me know what I did wrong in the comments. It ended up that writing my own custom script was faster, and made sure I understood exactly how the attack works. The script below does that for you. Simply provide the appropriate password as the first argument on the command line.

Here’s a demo of (a later version of) the script running:

I really enjoyed this level. The trial-and-error to figure out what SQL would be useful to inject was fun, and I learned a lot. I hope you did too. See you next time, for level 16.

Lessons learned

What seemed like a fairly limited opportunity for SQL injection turned out to be completely fatal. Don’t be injectable.