[安洵杯 2019] easy_serialize_php 反序列化字符逃逸
1 题目描述
打开题目页面,只有一个 “source_code” 链接,点击可以看到源代码。
2 代码审计
访问 ?f=highlight_file 获取源代码:
1 |
|
关键函数分析
1. filter() 过滤函数
过滤关键字:php、flag、php5、php4、fl1g,使用 preg_replace 将它们替换为空字符串。
2. extract($_POST) 变量覆盖
extract() 函数将 POST 数组中的键值对提取为 PHP 变量,这意味着可以通过 POST 覆盖前面的 $_SESSION['user']、$_SESSION['function']变量。
3. show_image 功能
当 $function == 'show_image' 时,会反序列化经过 filter 处理的 session 数据,然后读取 $userinfo['img'] base64 解码后的文件路径。
3 信息收集 — phpinfo
访问 ?f=phpinfo,在 phpinfo 页面中发现关键信息:
- auto_append_file:
d0g3_f1ag.php— 存在可疑 PHP 文件 - FLAG:
not_flag— 不是真正的 flag

由此可知 flag 很可能就在 d0g3_f1ag.php 文件中。
4 反序列化字符逃逸原理
核心漏洞点
filter() 函数会对 serialize($_SESSION) 的序列化字符串进行关键字过滤,而过滤后的字符串会被 unserialize() 反序列化。
当 filter 删除了序列化字符串中的字符,但长度标识(s:N)没有相应更新时,就会导致反序列化解析错位,从而实现字符逃逸。
三种 POST 方式实现逃逸
参考文章中给出了三种 POST 传参方式,均可以实现字符逃逸,可对比学习规律:
方法一:利用键名 flagflag
1 | _SESSION[flagflag]=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}" |
序列化后:a:1:{s:8:"flagflag";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"}",filter 删除两个 “flag” 后键名变为空,s:8 向后吞并,注入的 img 键值对被解析。
方法二:利用键名 flagphp
1 | _SESSION[flagphp]=;s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}" |
filter 删除 “php,flag” 后键名变化,同样造成逃逸。
5 构造 Payload 获取 Flag
第一步:读取 d0g3_f1ag.php
d0g3_f1ag.php 的 base64 编码:
1 | d0g3_f1ag.php -> base64_encode = ZDBnM19mMWFnLnBocA== |
构造 payload,利用 extract($_POST) 覆盖 $_SESSION['user']、$_SESSION['function']变量和使后面的 $_SESSION['img']变量逃逸:
通过字符逃逸,原本代码中设置的 img 值(guest_img.png 的 base64或sha1(base64_encode($_GET['img_path'])))被忽略,注入的 ZDBnM19mMWFnLnBocA== 成为了 img 的值。
发送请求:
1 | GET /index.php?f=show_image |
查看返回结果页面源代码:
1 |
|
成功读取到 d0g3_f1ag.php 文件!其中提示 flag 在 /d0g3_fllllllag 文件中。
第二步:读取 /d0g3_fllllllag 获取 Flag
/d0g3_fllllllag 的 base64 编码:
1 | /d0g3_fllllllag -> base64_encode = L2QwZzNfZmxsbGxsbGFn |
构造相同结构的 payload,只替换 base64 字符串:
1 | GET /index.php?f=show_image |
最终获取 flag:
6 知识点总结
extract()变量覆盖 — 通过 POST 数据覆盖$_SESSION、$function、$_GET等变量- 反序列化字符逃逸 — 利用 filter 删除关键字导致的序列化字符串长度与内容不匹配,实现反序列化注入
- PHP 反序列化容错性 — PHP 反序列化时能自动跳过无效字符继续解析,这为注入提供了可能
字符逃逸的通用思路
当序列化字符串经过替换/过滤操作后,长度发生变化时:
- 替换后变短(本题):利用长度标识 > 实际内容,向后吞并其他字段
- 替换后变长:利用转义字符导致闭合提前
