SQL注入部分讲解
若有错误,还请斧正。
1.SQL注入
攻击者可在正常的SQL语句中注入自己的语句,使得原SQL语句改变并执行则为SQL注入.
整型注入
注入点数据类型为整型则为整型注入,如下
输入的id不同,会从数据库中取出不同的结果。我们输入的id被直接拼接进sql语句中执行,这就使得我们可以直接接管sql。通过在id处注入sql语句来获取各种信息.
在这里我们可以构造联合查询payload来获取信息,首先需要判断查询语句的字段数
,当查询字段数大于等于这个数字时页面会给出正确执行的结果。
当查询字段数小于这个数字时页面会给出错误的执行结果
轮番操作后就可以确定在此处的查询字段数为3.而后便可以通过联合查询来确定结果显示位。
对了。此时mysql执行的sql语句为,在id部分我们需要一个数据库中没有的数据(比如0),
确定了结果显示位为联合查询的数字3处,由此可借由mysql系统库(information_schema)来获取表,如下
获取表的列信息如下
mysql的group_concat函数可以将查询结果合并为一个结果
在获取了表与列的信息和即可查询该表的具体数据
字符型注入
字符型注入与整型注入差别不大,两者最主要的差别在于字符型注入需要闭合引号.当我们注入一个引号时,mysql在执行时引号不成对,则会出错。
通过各种手段处理引号后则可以正常构造与整型注入类似的payload来获取各种信息.对于sql语句后面自带的引号可以采取注释、闭合等手段。
后面与整型相同,则不再赘述
引号处理
报错注入
在程序不输出查询结果,但会输出mysql错误信息时则可通过报错注入来获取信息.报错注入不需要像联合注入一样获取显示位,因为错误信息所在即是显示位.
报错注入通常使用以下函数
updatexml
报错用法
extractvalue
报错用法
floor
报错用法,概率报错
或
rand函数可在0-1之间生成一个随机数,而floor则是获得小于等于传入值的整数
当我们使用rand()*2时则可以获得一个0-2之间的随机数,倘若这时使用floor则可以获得一个0或者1
mysql可以使用group by 对结果进行分组,将结果相同的划为一组,count(*)可以统计结果数量
盲注
注入结果无任何回显即是盲注
布尔盲注
根据注入执行结果的布尔值(true,或false),页面显示不同,由此作为判断注入结果的依据即是布尔盲注
我们可以通过构造sql语句来逐步判断后端的结果,如下
当页面显示wow时,就说明database第一个字符为s,由此,进一步注入判断出当前数据库全名为sqli.
为提高注入效率,可编写脚本如下(已注释掉highlight_file(__FILE__))
该脚本相比于手工注入,效率有了很大的提升,但是该脚本的缺点还是很明显 --- 因为每一个字符都要进行判断,从而导致运行速度不够快
故我们可以使用二分法编写一个脚本,如下
延时盲注
在注入结果不会被输出,并且不管注入成功还是失败页面始终只有一个反应时,则可进行延时盲注,通过mysql执行需要一定时间的函数来作为注入正确与否的判断标准.
我的环境出了些问题,故这里用的CTFHUB->技能树->时间盲注
正常发起请求时响应时间如下,51ms
sleep配合if函数,延时payload
延时后的响应时间如下(依照不同环境,响应时间不同),1047ms=1.047s
故可以在if的第一个参数处构造sql语句,再通过响应时间来判断结果是否正确
猜解表名payload
猜解列名payload``1 and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='表名'),{第几个字符},1)<{字符ascii码},sleep(1),1)`
获取值 payload``1 and if(ascii(substr((select 列名 from 表名),{第几个字符},1)<{字符ascii码},sleep(1),1)`
为提高注入效率,编写脚本如下
延时盲注流程大抵如此,再罗列一些延时注入的函数如下
参考文章离怀秋
堆叠注入
通过分号(;)来结束一条语句并同时执行其他sql语句即是堆叠注入.
可以通过mysql预编译语句来执行查询语句
下面给一实例
强网杯2019 随便注
构造堆叠注入payload
查询数据库信息
查询数据库表信息
也可
大小写绕过限制
1';Set @a=0x73656c6563742067726f75705f636f6e636174287461626c655f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d64617461626173652829;Prepare b from @a;execute b;再构造
也可
1';Set @a=0x73656c6563742067726f75705f636f6e63617428636f6c756d6e5f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e636f6c756d6e73207768657265207461626c655f6e616d653d273139313938313039333131313435313427;Prepare b from @a;execute b;最后构造
1';Set @a=0x73656c65637420666c61672066726f6d20603139313938313039333131313435313460;Prepare b from @a;execute b;参考文章backlion
二次注入
在一个输入点将用户输入的含有特殊字符的字符串存入数据库,在某一个输出点将存入的字符串取出时未进行转义便拼接进另一处sql语句,所导致的sql注入即是二次注入.如下
实例
网鼎杯2018 Unfinish
只有一个登录、注册功能,成功登录后便进入主页!
尝试注册稀奇古怪的用户名
注册没有成功,尝试闭合单引号
注册成功,登录查看.确定是sql注入
在本地测试后发现我们注入在insert中的语句可构成布尔型结果,故可进行布尔盲注
fuzz一下哪些字符被过滤,哪怕bp设置1线程,也会被buuctf的限制请求干扰到,所以写脚本
information被过滤了我们无法获取到表名等信息(sys数据库不一定存在),这里猜表名为flag,直接因为并不能确定列名(先测试了select flag from flag ,会出错)
先来确定我们的布尔盲注
没问题,写脚本
2.题
CTFHUB
2017-赛客夏令营-Web injection
整型注入
判断列数,
获取回显位
表名
字段名
爆值
BUUCTF
WUSTCTF2020 颜值成绩查询
经过一番测试发现,stunum为数字布尔注入.
经fuzz,发现select被过滤,但可大写select绕过
故编写脚本如下
BJDCTF 2nd 简单注入
访问robots.txt发现提示文件hint.txt
fuzz过后发现过滤了以下关键字
单、双引号皆被过滤。要想注入语句首先需要逃逸单引号,这里很简单提交即可将sql语句自带的一个单引号转义,进而逃逸出一个引号.而后在password处构造语句来注入数据
and被过滤,但是or可用,通过or + 异或操作使执行结果出现不同布尔值,发现页面响应信息不同.
测试payload与
故编写脚本如下
SUCTF 2018 MultiSQL
正常注册后登录
编辑头像处,可以上传图片
用户信息与注册处存在sql注入.前者为堆叠注入、后者为二次注入。这里使用堆叠注入写入webshell
payload
CISCN2019 day2 easyweb
在robots.txt中发现备份文件.下载
首先需要思考单引号的闭合问题。
因为addslashes的存在无法直接使用引号闭合,也无法使用来逃逸单引号。但是代码11-12行对传入数据的替空处理可做利用。
构造一个即可逃逸单引号。(经过addslashes处理变成,再经过str_replace的处理,将替换为空,则得到一个)
故,可构造payload,根据页面响应内容进行布尔判断.
二分脚本
登录
上传常规shell,后缀不能为php,使用phtml绕过
查看该文件,是用户的上传记录,记录了上传的文件名
考虑修改文件名为shell,因为文件名会检测关键字php,所以使用php短标签
再访问日志文件
RCTF2015 EasySQL
程序只有几个功能:注册、登录、修改密码、查看文章
在注册时发现有过滤一些敏感词,fuzz如下
测试后发现注册点username处存在二次注入
过滤了and(忽略大小写)但没过滤,尝试进行报错注入
admin4"&&updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))),1)# admin4"&&updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag'))),1)# admin4"&&updatexml(1,concat(0x7e,(select(flag)from(flag))),1)#, flag not here老折磨怪了
admin4"%26%26updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users'))),1)#通过这几次的结果可以看出,输出结果有长度限制,但是这不妨碍我们猜到字段为real_flag_1s_here
常用的字符截取函数都被过滤了
长度690,真flag藏在里面,可以
先尝试将所有结果翻转,可以看到是flag的一部分
admin4"&&updatexml(1,concat(0x7e,(select(reverse(group_concat(real_flag_1s_here)))from(users))),1)#需要想办法取剩下的一部分.这里尝试通过正则获取结果,rlike被过滤了,但是可以使用rlike的同名函数regexp
admin4"&&updatexml(1,concat(0x7e,(select(real_flag_1s_here)from(users)where(real_flag_1s_here)regexp('^flag'))),1)#两者组合
给个半自动脚本
NCTF2019 SQLi
给了sql语句
hint
拿到密码即可getflag,ban了很多字符,截取字符的函数substr不能用,需要()的函数也都不能用
但是这里可以使用正则匹配来曲线救国
首先,使来逃逸引号,而后构造payload使得结果为true和false查看页面不同
,这里的%00并非真实的%00而是ascii码为0的字符,用该字符截断sql语句后面的引号(注释符被过滤)
当查询语句为真时,页面会进行重定向
可以将状态码作为判断正确与否的标准
故编写脚本如下
CISCN2019 华北赛区 Day1 Web5 CyberPunk
读取源码
还发现提示文件hint.php
经过一番审计后,在search.php中发现一个注入点
过滤了一些注入关键词,无法深入注入,再找其他地方
在change.php中发现二次注入
参数未经正则检验,只使用了addslashes转义字符
在代码第21行,可以看到在更新地址时它不但修改了当前地址,还保存了旧地址。由此二次注入产生
构造payload验证
编写脚本如下
结果只会显示31位
测试发现存在flag.txt,读取该文件
GYCTF2020 Ezsqli
fuzz可用字符
结果如下
substring被过滤,但是substr可以用
构造payload进行测试
布尔型结果,故可编写脚本了。
因为information_schema被过滤了,所以我们使用sys数据库来获取表信息
脚本如下
flag