文章总结: 本文解析SQLi-Labs第35至37关,演示绕过mysql_real_escape_string的宽字节注入。核心漏洞在于连接字符集与数据库实际解析集不一致,导致转义失效。通过注入%df’等Payload利用字符编码差异闭合引号,成功获取数据。结论指出仅靠转义函数不可靠,需采用预编译语句、统一字符集并杜绝SQL字符串拼接以确保安全。 综合评分: 90 文章分类: WEB安全,渗透测试,CTF,漏洞分析
“我都用 mysql_real_escape_string 了,怎么还会被注入?”——sqli-labs35~37通关解析
原创
武文学网安 武文学网安
武文学网安
2026年1月26日 05:42 中国香港
大家好,我是武文。
经过前面的学习,让我们来快速通过这机关。 一、35关
第 35 关有个小坑,如果一直沿用前几关“找闭合”的惯性思维,很容易卡住。这一关其实是在提醒我们:并不是所有注入都依赖引号闭合。 它更像是一个过渡关卡,用来打破“必须闭合才能注入”的心理预期。
?id=1 and 1=1?id=1 and 1=2?id=1 and updatexml(1,concat(0x7e,database(),0x7e),1)
二、36/37关
2.1 整体说明
| 项目 | 说明 | | — | — |
| | | | — | — | | 请求方式 | * 第 36 关:GET * 第 37 关:POST 请求方式不同,但漏洞成因完全一致 |
| | | | — | — | | 页面回显 | 有报错 |
| | | | — | — | | 注入类型 | 显错注入 / UNION |
| | | | — | — | | 防御函数 | mysql_real_escape_string() |
| | | | — | — | | 核心考点 | 字符集不一致导致转义失效 |
| | | | — | — | | 本质问题 | 连接字符集 ≠ 实际解析字符集 |
这两个使用的防御函数是mysql_real_escape_string(),核心功能是对字符串中的特殊字符(如反斜杠、单引号、换行符等)进行转义,确保输入数据在 SQL 语句中安全使用。第 36 与 37 关的区别仅在于请求方式(GET/POST),核心漏洞完全一致,因此放在一起讲。
先说结论:mysql_real_escape_string() 依然不是“银弹”
必须先明确一个事实:mysql_real_escape_string() 比 addslashes() 更“聪明”,但仍然不是安全方案。和 addslashes() 不同,mysql_real_escape_string() 并不是“无脑加反斜杠”, 它的问题不是“不知道多字节”,而是“相信了错误的字符集环境”。
它的安全性高度依赖一个前提条件:
👉 数据库连接字符集,与数据库实际解析字符集完全一致
一旦这个前提不成立—— 防御立刻失效。
2.2、mysql_real_escape_string() 到底“强”在哪里?
相比 addslashes(),
mysql_real_escape_string() 做了两件更“高级”的事:
- 会根据当前连接字符集进行转义
- 只转义当前字符集下有风险的字符
例如在 GBK 连接下,它知道:
- 哪些字节组合是合法字符
- 哪些字节需要被保护
👉 听起来是不是很安全?
问题恰恰就出在:字符集“是谁说了算”。
2.3 mysql_real_escape_string() 的工作流程
用户输入
↓
PHP 调用 mysql_real_escape_string()
↓
基于「连接字符集」进行转义
↓
SQL 发送给 MySQL
↓
MySQL 基于「实际字符集」解析
漏洞出现的条件只有一个:
连接字符集 ≠ MySQL 实际解析字符集
2.4 第 36 / 37 关的关键配置
在这一关中:
-
PHP 连接 MySQL 时:
-
使用的是 latin1 / utf8
-
MySQL 实际表 / 数据库:
-
使用的是 GBK
结果就是:
mysql_real_escape_string() 在“错误的字符集假设”下工作
三、绕过原理:宽字节再次登场,但角色变了
在 addslashes() 关卡中,问题是:“它假设一个字符 = 一个字节”
而在 mysql_real_escape_string() 中,问题升级成:“它在错误的字符集语境下判断风险字符”
#
3.1 攻击者构造的核心字节结构
依然是熟悉的套路:
[GBK 高字节] + ‘(0x27)
例如:
%df%27
3.2 转义为何会失败?
这里的关键不是“宽字节有多神奇”, 而是 mysql_real_escape_string 判断风险字符时,站错了字符集立场。假设:
- 连接字符集:latin1
- 数据库解析字符集:GBK
PHP 侧(latin1 视角)
0xDF → 普通字符0x27 → 单引号 → 被转义为 \’
结果:
0xDF 0x5C 0x27
MySQL 侧(GBK 视角)
0xDF 0x5C → 一个合法 GBK 汉字0x27 → 单引号
👉 转义再次被“吃掉”
四、第 36 关实战验证
第37关参照从GET到POST:addslashes()绕过的本质突破——sqli-labs 34 关实战
4.1 注入点确认
?id=1 正常?id=1′ 报错(被转义)

说明:
- 注入点存在
- 引号被处理
4.2 宽字节闭合测试
?id=1%df’?id=1%df’ –+

出现 SQL 语法错误,说明:
%df成功参与解析- 单引号重新获得语义
4.3 显错注入
?id=1%df' and updatexml(1,concat(0x7e,database(),0x7e),1)--+
#
页面成功报错并回显数据库名。
4.4 UNION 注入验证
?id=-1%df’ union select 1,2,3–+?id=-1%df’ union select 1,database(),3 –+

成功获取数据库信息。
第 36 / 37 关正式通关。
结语
这两关“现实意义极高”?
因为它们揭示了一个非常残酷的事实:就算你用的是“正确的防御函数”,只要字符集配置错了,一切都是白搭。
在真实环境中,这种情况极其常见:
- 老项目数据库是 GBK
- 新代码默认 UTF-8 / latin1
- 没有人检查连接字符集
- 防御代码“看起来完全没问题”
真正可靠的,只有:
- 预编译 / 参数化查询
- 明确、统一的字符集策略
- 拒绝字符串拼接 SQL
这也是为什么,真正的防御思路从来不是“我用了什么转义函数”,而是: 👉 我是否彻底避免了“字符串拼接 SQL”这个行为本身。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:武文学网安 武文学网安 武文学网安《“我都用 mysqlrealescape_string 了,怎么还会被注入?”——sqli-labs35~37通关解析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论