[+] Blinded by the Light

Every human being has a basic instinct: to help each other out. If a hiker gets lost in the mountains, people will coordinate a search. If a train crashes, people will line up to give blood. If an earthquake levels a city, people all over the world will send emergency supplies. This is so fundamentally human that it’s found in every culture without exception. Yes, there are a**holes who just don’t care, but they’re massively outnumbered by the people who do. – The Martian

Few days ago, a friend asked me to solve two SQL Injection challenges on WeChall. At first, I thought this would be some regular SQL injection but I was wrong. Actually, these were quite good with some tricky rules making a great case scenario. So, I’ve decided to do a write-up and blow some dust off my blog.

Challenge 1 – Blinded by the Light

  • The mission is to extract an MD5 password hash out of a database.
  • The limit for this blind SQL injection is 128 queries max.
  • The source code of the vulnerable script is given here and we are allowed to execute a reset.

Challenge 2 – Blinded by the Lighter

  • This challenge is a sequel to the “Blinded by the light” challenge.
  • Again the mission is to extract an MD5 password hash out of the database.
  • This time, the limit for this blind SQL injection is 33 queries max.
  • Also we have to accomplish this task 3 times consecutively and under 27 minutes.
  • The source code of the vulnerable script is given here and we can execute a reset.

Now if you guys want to try these challenges all by your self, you should stop right HERE. Or HERE. I see you’re not stopping. So let’s get started.

Challenge 1 – Blinded by the Light

In Blind SQL injection, we don’t get the output of our queries directly on the page. We observe the page response to extract the data and this can be a real pain sometimes. We have to ask the database a series of true or false questions and determine the answers based on the page response. Keeping that in mind, extracting 32 characters using maximum 128 queries, makes it a lot more difficult than I thought.

So in the face of overwhelming odds, I’m left with only one option. I’m gonna have to science the sh*t out of this. – The Martian

Let’s break it down a little. We need to extract an MD5 password hash.

Length of MD5 Hash : 32 Characters
Max Queries allowed : 128
Queries per Character : 128/32 = 4

So, we have exactly 4 queries to extract one character. Apparently, this looks impossible. But then the fact that we need to extract an MD5 hash, shows some light in the darkness.

MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit Hexadecimal Number. Wikipedia

Now we know that we don’t need to check every possible printable character because hexadecimals have a limited charset of 16 characters i.e. “0123456789abcdef”. An ASCII character can be represented using 8 bits and if I consider 1 as TRUE and 0 as FALSE, I still need 8 queries to extract one single character of this MD5 hash. But again, there’s something great about these hexadecimals, their first 4 bits are always 0 so we need to extract only last 4 bits.

It can’t be our alphabet. 26 characters plus a question card into 360 gives us 13 degrees of arc. That’s way too narrow. I’d never know what the camera was pointing at … … Hexadecimals! Hexadecimals to the rescue. – The Martian

ASCII Character First Four Bits Last 4 Bits
0 0000 0000
1 0000 0001
2 0000 0010
3 0000 0011
4 0000 0100
5 0000 0101
6 0000 0110
7 0000 0111
8 0000 1000
9 0000 1001
a 0000 1010
b 0000 1011
c 0000 1100
d 0000 1101
e 0000 1110
f 0000 1111

Now all I have to do is to come up with a MySQL payload to convert each character of the MD5 hash to binary and then extract the last 4 bits (0s: FALSE or 1s: TRUE) one by one. So, that makes it 4 (bits) x 32 (characters in MD5) = 128 (queries). Here’s my final payload.

This payload will be executed inside two nested loops. The outer loop will iterate 32 times changing the ‘x’ in this payload from 1 to 32. This loop represents the length of the MD5 hash. The inner loop will iterate 4 times for each character to extract 4 bits one by one changing ‘y’ in this payload from 1 to 4. The conv() function in this payload converts a number or a character from one base to another. So, Conv(‘a’, 16, 2) will convert ‘a’ from base 16 (Hexadecimal) to base 2 (Binary). Another issue here is that MySQL tends to ignore the leading zeros returned by the Conv(). So, Lpad(‘a’, 4, ‘0’) makes sure that we have exactly 4 bits. For example, character ‘5’ will be returned as ‘101’ by Conv(), which may create problems because our inner loop is looking for 4 bits and not 3. So Lpad(‘101’, 4, ‘0’) will add a leading zero to this value and make it ‘0101’ again. The challenge page returns TRUE with the following message.

blinded1

And this is the FALSE response.

blinded2

Let’s code.

This script automatically makes a login session for our wechall account, resets the attempt counter and solves the challenge.

blinded3

blinded4

blinded5

Challenge 2 – Blinded by the Lighter

Let’s take a look at the rules. We have to perform blind SQL injection to extract an MD5 hash (32 characters) 3 times in a row in 27 minutes i.e. 9 minutes/hash using max 33 queries/hash.

I admit it’s fatally dangerous, but I’d get to fly around like Iron Man. – The Martian

I have realized that I’d have to play with MySQL Sleep() function here. For those of you who don’t know, Sleep() creates delay for the number of seconds given by the duration argument and pauses the execution. We can actually get one character per query using Sleep(). For example, I can ask the database if the first character of the password is ‘a’, sleep for 1 second, if it’s ‘b’, sleep for 2 seconds, if it’s ‘c’, sleep for 3 seconds and so on. And by measuring the execution time we can easily figure out the results. But there’s a reason why people don’t use it. There’s no way we can measure accurate time from the users end unless you have super fast internet with less than 20 ms ping delay from the target (like you’re on the same LAN segment). But luckily, I don’t have to measure the execution time because it’s already given at the bottom of the page. Wechall shows some server side values in the footer of every page like total execution time, number of queries executed, number of modules loaded etc.

blinded6

So, considering all the dynamics and making some minor adjustments, here’s my code.

I’ve chosen the time step of 1.5 seconds on line 41. So, the last character in my charMap (i.e. ‘9’) will take maximum 16 x 1.5 = 24 seconds. Now, If I consider the worst possible scenario that every character in my password hash is ‘9’ (Which will never be the case), it’ll take 24 x 32 (MD5 Length) = 768 seconds = 12.8 Minutes in worst case. So I guess, 1.5 seconds won’t be the ideal time step but it’ll do just fine. If you have good internet connection (and you’re feeling lucky), you can always change this value on line 41 and reduce it to 1 second. I had to run this script 2 – 3 times to make it work (because my internet sucks) but eventually it worked.

blinded7

blinded8

blinded10

blinded12

At some point, everything’s gonna go south on you… everything’s going to go south and you’re going to say, this is it. This is how I end. Now you can either accept that, or you can get to work. That’s all it is. You just begin. You do the math. You solve one problem… and you solve the next one… and then the next. And If you solve enough problems, you get to come home. All right, questions? – The Martian

GitHub Repository