有时候失败了也能学到很多东西。
这是一个提交数据的接口(诈骗),其携带了一个htable的参数,很明显,在设计之初,这里可以传递不同的表名,来把数据存储在不同的表里。而表名列名这种无法进行参数化查询,容易发生SQL注入。
当我们随便输入一个参数时,就会报错,xxx表不存在。
可以看到它在sql语句的构造是www.fa_xxx的形式,那么这个xxx就是常规表名,我们可以猜测一下,比如admin。
输入admin和admind的报错不一样,证明存在admin表。报错truename列名,证明当前表中存在这个列名(同时也是http传参的参数),那么我们需要将当前的表名给爆破出来。如果爆破不出来,由于前面有www.fa_的前缀,这里是无法注入的。
这里用sqlmap的字典爆破出来了,当前表是info。
接着去尝试利用报错,爆出当前SQL语句。
info and就行了,可以看到后面爆出了SET,也就是原本的语句如下。
update www.fa_info SET xxx=xxx where id=1;
当然后面set有很多问号,这证明后面的都用的参数化查询,无法SQL注入。
这里加and就会报错,但如果and替换成非SQL专用词,比如andd,就不会报错。这是update语句的特性。
那我们依葫芦画瓢,利用注释闭合后面的语句,然后自己写正常的语句进去,注意一定要加个where条件,否则可能整张表都被修改了。
结果报错了,为什么会报错呢?难道是有换行无法注释?并不是,经过反复尝试,发现传参无法大写,大写的字母都会加上下划线。
不过这个防护没有什么所谓,直接小写set就行。
到这一步算是完成注入payload的基本构造了,虽然还是会报错,但这里报错应该是其他地方的报错,注入的语句应该已经在数据库执行了。
然后就是怎么注数据,这里报错注入,时间盲注,布尔盲注应该都行。其中布尔盲注必须要构造case where的报错和不报错的布尔条件,普通的布尔盲注是不行的。我先尝试了一下最简单的sleep()。
结果居然报错列名,又经过反复尝试,确认了是圆括号的问题,就算把圆括号用单引号包裹当作普通字符串,还是会报错。应该是代码自带过滤器将其过滤了。
思考一下不用圆括号该如何注入。有三种情况。
1,联合注入。
联合注入注值是不需要圆括号的,虽然注表名和列名需要,但可以猜测。
2,into outfile写文件。
需要root,需要绝对路径,需要secure_file_priv为空,需要mysql本身有权限,还是比较苛刻的。
3,堆叠注入。
用分号来分割SQL语句,但极少有支持堆叠的。
由于这是一个update注入,1和2都不用考虑了,剩下的只有3,来尝试一下。
结果并不行,不支持堆叠。
后来又想了一下,既然是update注入,我可以完整的控制set之后的语句,我直接修改admin表的内容不就行了吗?
结果也不行,尝试换表发现,虽然可以爆破表名,但除了info,填其他的正确表名都会出现这个报错,不管后面是什么语句。出现这个报错之后SQL语句也并没有真的执行。那么这次的SQL注入就到这里了。
原文始发于微信公众号(珂技知识分享):一次失败的SQL注入