0x00前言
代码审计发现了一个基于时间延迟的SQL盲注,但是发现 sleep(2)成功获取数据时页面返回时间均为4秒,即为设置时间的两倍,所以有了下面的分析。
0x01Mysql 执行优先级和sleep函数
Mysql语句关于AND和OR执行优先级和Mysql查询优化策略有以下几点:
- AND的优先级大于OR,先计算AND的结果,再计算or的结果
- 同优先级AND中如果有恒为0(如and 1=2),则直接返回0,不执行同级中的其他语句
- 多个OR连接的语句中如果有恒为1(如or 1=1),则直接返回1,不执行的其他语句
首先测试数据表结构和数据如下:
1
2
3
4
5
6
7
8
9mysql> select * from ctf;
+--------+----------+-----------+------+
| userid | username | signature | mood |
+--------+----------+-----------+------+
| 1002 | test2 | test2 | 1 |
| 1001 | test1 | test1 | 1 |
| 100 | 32313333 | test | 0 |
+--------+----------+-----------+------+
3 rows in set (0.00 sec)执行select * from ctf where mood=sleep(1),延时了3秒。
(没有限定条件,和数据表的每一条进行对比,进行了3次sleep(1)函数)1
2
3
4
5
6
7mysql> select * from ctf where mood=sleep(1) ;
+--------+----------+-----------+------+
| userid | username | signature | mood |
+--------+----------+-----------+------+
| 100 | 32313333 | test | 0 |
+--------+----------+-----------+------+
1 row in set (3.00 sec)执行select * from ctf where username=’test2’ and mood=sleep(1);延时1秒。
(先筛选username=’test2’,只有1条记录,进行了1次sleep(1)函数)1
2mysql> select * from ctf where username='test2' and mood=sleep(1);
Empty set (1.00 sec)执行 select * from ctf where userid>100 and mood=sleep(1);延时2秒。
(先筛选userid>100,有2条记录,进行了2次sleep(1)函数)1
2mysql> select * from ctf where userid>100 and mood=sleep(1);
Empty set (2.00 sec)select * from ctf where userid>100 and 1=2 and mood=sleep(1);没有延时直接返回空。(由于sleep之前有and 1=2,不可能成立,直接返回空,没有执行sleep(1))
1
2mysql> select * from ctf where userid>100 and 1=2 and mood=sleep(1);
Empty set (0.00 sec)执行select * from ctf where userid>100 and 1=2 or mood=sleep(1);延时3秒
(根据优先级,先计算执行100 and 1=2,不可能成立,没有符合要求的数据记录,但是还需要判断or之后的条件,所以仍然要查询比对三次,执行3次sleep(1))1
2
3
4
5
6
7mysql> select * from ctf where userid>100 and 1=2 or mood=sleep(1);
+--------+----------+-----------+------+
| userid | username | signature | mood |
+--------+----------+-----------+------+
| 100 | 32313333 | test | 0 |
+--------+----------+-----------+------+
1 row in set (3.00 sec)执行select from ctf where userid>100 and 1=sleep(1) or mood=0;延时2秒,执行select from ctf where 1=sleep(1) and userid>100 or mood=0;延时3秒,两者返回结果一致,但是由于执行顺序不同,导致延时不同。(前一种情况根据优先级,执行userid>100 and 1=sleep(1),通过userid>100筛选只有两条记录,然后再sleep(1),所以只延时2秒。后一种情况,根据优先级,执行1=sleep(1) and userid>100,直接利用1=sleep(1)的条件和数据库记录对比以后,再判断userid>100的条件,要全部比对执行三次,所以延时3秒。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14mysql> select * from ctf where userid>100 and 1=sleep(1) or mood=0;
+--------+----------+-----------+------+
| userid | username | signature | mood |
+--------+----------+-----------+------+
| 100 | 32313333 | test | 0 |
+--------+----------+-----------+------+
1 row in set (2.00 sec)
mysql> select * from ctf where 1=sleep(1) and userid>100 or mood=0;
+--------+----------+-----------+------+
| userid | username | signature | mood |
+--------+----------+-----------+------+
| 100 | 32313333 | test | 0 |
+--------+----------+-----------+------+
1 row in set (3.00 sec)执行select * from ctf where userid>100 and 1=sleep(1) or mood=0 or 1=1;延时0秒。
(userid>100 and 1=sleep(1) or mood=0 or 1=1,在OR连接的语句中有恒成立的1=1,所以直接返回结果,没有延时。)1
2
3
4
5
6
7
8
9mysql> select * from ctf where userid>100 and 1=sleep(1) or mood=0 or 1=1;
+--------+----------+-----------+------+
| userid | username | signature | mood |
+--------+----------+-----------+------+
| 1002 | test2 | test2 | 1 |
| 1001 | test1 | test1 | 1 |
| 100 | 32313333 | test | 0 |
+--------+----------+-----------+------+
3 rows in set (0.00 sec)
0x02小结
由于MySQL的条件优先级的不同,在不同语句中执行sleep()函数导致的延迟时间(执行次数)不同,一个比较简单的判断就是,判断sleep()函数所在的点,进行数据查询时需要对比的数据记录数,即等于sleep()函数执行的次数。