从任意文件读取到phar反序列化:CISCN Dropbox综合Web题利用链复现

发布时间:2026/10/20 16:10:28

从任意文件读取到phar反序列化:CISCN Dropbox综合Web题利用链复现 把BUUCTF上这道『CISCN2019 华北赛区 Day1 Web1 Dropbox』拉到本地复现的时候我一开始是有点轻敌的。CTF Web题见多了看到“网盘”两个字就会下意识觉得无非是文件上传绕过呗。直到注册登录进去面对一个五脏俱全的Dropbox管理界面我才意识到这题考的不是单个漏洞而是“任意文件读取 源码审计 phar反序列化 symlink组合”整条链。这篇文章就是我当时完整复现的记录包括踩坑过程适合正在刷BUUCTF、想搞明白CISCN这类偏综合Web题的人参考。题目环境一直在不需要额外搭建跟着请求走一遍就能把整条利用链串起来。这种综合性题目的好处是一道题串起好几个考点每个考点的存在感都很强。坏处是如果你只盯着“上传能不能绕过”会浪费大量时间在错误的方向上。我复现时的第一条经验就是——先把功能面摸清楚再决定从哪里下手而不是看到上传框就往上撞。1. 从登录框到文件管理Dropbox的功能面到底铺了多大1.1 先别急着打把页面上的按钮全部点一遍打开题目页面第一眼是一个登录/注册框。注册一个账号进去看到一个很标准的文件管理界面上传区、文件列表、下载按钮、删除按钮。表面上功能只有这几个但真正要做的不是急着传一句话木马而是把每个按钮背后的请求都抓下来搞清楚服务端到底接收了哪些参数。我当时的做法很简单先传一个正常的test.txt内容随便写然后分别点击下载、删除把对应的请求记录下来。上传用的是POST带filename字段和文件内容下载是GET关键参数filename删除也是POST同样带filename。这里一个很明显的特征是三个功能都在用filename这个参数但各自对它的处理逻辑可能完全不同而这正是漏洞的温床。1.2 上传/下载/删除三条请求的参数特征我用Burp逐个把请求记录下来整理成了一张表这样后面审计的时候思路清晰很多功能请求方式关键参数初步风险判断上传POSTfilename 文件内容文件类型/内容过滤下载GETfilename路径穿越/任意文件读取删除POSTfilename路径穿越/任意文件删除列表GET无信息泄露实际测试时删除功能传入../../../../etc/passwd是失败的说明删除侧对路径有校验或拼接方式不同。上传功能只检查了文件扩展名但并没有严格限制内容传一个.php会被拦截传一个.txt就放行。这个上传过滤看起来比较粗糙但也没有明显到可以直接利用的程度。综合来看最值得深入的反而是下载接口——它直接暴露了filename参数且响应是文件内容天然适合做路径穿越测试。1.3 用“功能-参数-风险”表代替盲打很多新手打CTF容易陷入“看到一个功能就猛打”的状态比如看到上传就去测绕过看到登录就去测注入。但实际上像Dropbox这种多功能的题真正的考点往往藏在不同功能对同一个参数的差异化处理里。我的建议是在纸上把每个接口的参数、方法、可能的漏洞类型列出来先做一轮快速试探再决定深入方向。试探不一定要用特别逆天的payload就是普通的../../../../etc/passwd、../flag、....//....//这类基础变形。这一轮做完基本上就能判断出哪个接口是“突破口”哪个接口是“辅助功能”。对我这轮复现来说突破口就是下载接口的filename参数。2. 下载接口的filename参数一次目录穿越牵出全部源码2.1 目录穿越的验证从passwd到源码下载接口的请求形如GET /download.php?filenametest.txt我直接改成GET /download.php?filename../../../../../../etc/passwd响应直接返回了passwd文件内容说明这里是任意文件读取而且没有对..做过滤。有了这个能力第一反应当然是去读flag但实际环境中flag通常不会直接放在网站根目录也不一定叫flag这个名字。更聪明的做法是先读源码搞清楚站点目录结构、后端逻辑、有哪些文件、哪些类再决定怎么拿到flag。这里有个经验任意文件读取漏洞产生后首要目标永远是源码而不是直接猜flag路径。因为源码会告诉你真正的攻击面在哪里也会告诉你怎么才能最高效地拿flag。直接猜路径的效率极低而且很容易猜错方向。2.2 能读不能读的边界download和delete为什么不一样为什么download能穿越delete却不能原因往往是两个功能对filename的拼接和校验方式不同。download的代码逻辑大概率是把uploads/目录和filename做字符串拼接然后直接readfile或file_get_contents没有任何realpath校验所以..能直接穿出去。delete则很可能用了realpath做校验或者把文件路径限制在了上传目录下先判断文件是否存在再执行删除。这么一来delete就不具备任意文件删除的能力至少不能通过简单的目录穿越实现。这一点很重要同一个参数在不同功能中的处理逻辑可能完全不同不能因为一个接口存在漏洞就假设其他接口也存在同样的问题。2.3 从报错信息和include链里反推目录结构有了任意文件读取但不知道源码放在哪怎么定位我用的办法是先读/etc/nginx/nginx.conf或Apache配置找到站点根目录。实际测试时这类网盘题目常见的源码路径是/var/www/html/但为了稳妥我故意请求了一个不存在的文件比如GET /download.php?filenameno_such_file_abc.php如果页面返回“文件不存在”之类的错误或者包含详细的路径信息就能直接得到物理路径。我当时拿到的是类似/var/www/html/xxx的路径。知道根目录后顺着读取/var/www/html/index.php就能看到首页逻辑再根据index.php里的include/require语句把class.php、config.php、upload.php这些文件逐个读出来。整个源码审计的起点就是从一次普通的路径穿越开始的。3. 源码里的漏洞信号File类的析构函数为什么让我眼前一亮3.1 还原index.php的请求流转拿到源码后我先把index.php从头到尾读了一遍。整体逻辑大致是这样的通过action参数控制路由分别处理文件列表、上传、下载、删除。每个分支都会做对应的参数校验但校验强度不一。比如下载分支几乎没校验删除分支做了简单的目录限制上传分支限制了扩展名但允许ZIP解压。这里最让我注意的一点是上传功能不仅保存了文件还支持ZIP压缩包解压。这意味着我上传一个.zip服务端会用解压逻辑把它展开到上传目录。这个功能本身就是个很大的攻击面——如果解压逻辑不严格就可能引入符号链接文件、phar文件等特殊条目。3.2 FileList与File这两个类各管什么继续往下看源码中存在两个核心类一个是文件列表相关的类另一个是文件操作相关的类。文件列表类主要负责遍历上传目录、展示文件名和大小文件操作类则封装了文件的创建、读取、删除。简化还原之后文件操作类的结构大致是这样class File { public $filename; public function __construct($filename) { $this-filename $filename; } public function __destruct() { if (file_exists($this-filename)) { unlink($this-filename); } } }看到这个类的时候我眼睛一亮。__destruct()里直接调用了unlink()而且$this-filename是对象属性完全可控。这就是一个标准的反序列化利用点只要能让这个类被反序列化并且控制filename属性的值就能在对象销毁时删除目标机器上的任意文件。3.3 __destruct里的unlink反序列化的标准信号为什么看到析构函数里有unlink就要往反序列化上想因为在正常的业务逻辑里这个类可能只是用来操作上传文件的filename通常来自用户上传时传入的文件名。但如果我能控制filename属性的值并让这个对象在某个时刻被销毁unlink就会执行而且不受任何路径校验限制。那怎么触发反序列化这就引出了phar反序列化的经典利用链。PHP中很多文件操作函数都可以通过phar://协议触发反序列化比如file_exists、is_file、file_get_contents。而代码里几乎所有文件操作都会先判断文件是否存在这正好满足了触发条件。3.4 攻击面清单哪些点可以组合成利用链审计完源码后我在笔记里列了一张攻击面清单下载接口存在任意文件读取可以用来读源码、读配置、读flag。上传功能支持ZIP解压意味着可以上传包含特殊条目的压缩包包括符号链接和phar文件。文件操作类的析构函数中存在可控的unlink可以通过phar反序列化触发任意文件删除。删除接口虽然做了路径限制但如果结合符号链接可以绕过目录限制。这四个攻击面单独看都不算特别致命但组合起来就能构成一条完整的利用链。尤其是任意文件读取和ZIP解压这两个能力叠加等于给了我在目标服务器上放置文件、随后读取文件的双重能力。4. phar反序列化把磁盘上的文件变成代码执行入口4.1 phar为什么能被当成反序列化入口phar是PHP的一种归档格式可以把多个文件打包成一个phar文件类似Java的jar。phar文件内部有一个metadata区域这个区域会被PHP序列化存储。当PHP的任何文件函数接触到一个phar文件时会先尝试解析该文件并反序列化metadata中的内容。如果metadata中正好是一个对象那么在反序列化时会触发对象的__wakeup方法在随后的对象销毁过程中还会触发__destruct方法。这就是phar反序列化的核心原理不需要代码里存在unserialize()函数只要代码在某个地方对用户可控的文件路径执行了file_exists、is_file、file_get_contents、stat等操作而这些操作是支持phar://协议的那么反序列化就会被触发。我在这道题里看到代码中大量使用file_exists来判断文件是否存在所以几乎可以确定这条路是通的。4.2 本地生成恶意phar的过程要在本地生成一个合法的phar文件需要修改PHP配置把phar.readonly设为Off否则PHP不允许创建phar文件。生成payload的代码很简单?php class File { public $filename; } $phar new Phar(payload.phar); $phar-startBuffering(); $phar-setStub(GIF89a?php __HALT_COMPILER(); ?); $object new File(); $object-filename /var/www/html/uploads/delete_me.txt; $phar-setMetadata($object); $phar-addFromString(test.txt, test); $phar-stopBuffering();这里有几个细节要注意。第一stub不是随便写的__HALT_COMPILER();后面必须紧跟协议要求的格式否则phar文件可能无法被正确解析。第二我在stub前面加了GIF89a这是一种常见的伪造文件头技巧目的就是绕过上传功能对文件内容的简单检查。第三metadata中保存的是File类的对象反序列化后会生成一个filename属性指向目标文件的实例当这个对象被销毁时unlink就会执行。4.3 把phar送到目标机器上传zip这个入口的作用生成了payload.phar之后怎么把它传到目标服务器上直接上传.phar扩展名很可能被拦截而且题目也有扩展名过滤。但有一个方法可以绕过把phar文件伪装成内容合法但扩展名为其他格式的文件甚至直接把它打包进ZIP文件。正好题目支持ZIP上传并自动解压。那我就把payload.phar改名为test.jpg然后打包成ZIP上传。服务端解压后上传目录里就会多出一个内容其实是phar、但扩展名是jpg的文件。之后只要让某个文件操作函数以phar://协议读取这个文件反序列化就会触发。4.4 触发与观察如何确认析构函数真的执行了触发的方式也很直接下载接口支持任意文件读取很多时候服务端在处理下载时会先对文件路径执行file_exists或者realpath。此时如果把filename参数设置为phar://uploads/xxx.jpg就可能触发反序列化。实际操作中我在uploads目录下创建了一个测试文件delete_me.txt然后构造了filename指向delete_me.txt的payload.phar上传后触发。之后再次访问文件列表发现delete_me.txt已经不见了说明__destruct里的unlink确实执行了。这一步验证非常重要因为只有确认了phar反序列化能触发后面的综合利用才站得住脚。5. symlink与unlink的联合作战最终读取flag的完整请求链5.1 制作带符号链接的zipln -s和zip -y拿到了反序列化删除任意文件的能力之后我们离flag还差一步如何读取到flag文件的内容任意文件读取虽然能穿越目录但穿越到根目录之后并不能直接列出所有文件只能靠猜路径。此时需要另一个文件系统技巧符号链接。思路是这样的如果上传的ZIP解压后能生成一个指向/的符号链接目录那么下载接口在拼接路径后就会顺着这个符号链接访问根目录下的任意文件。制作这个ZIP只需要在Linux环境下执行ln -s / rootlink zip -y symlink.zip rootlinkzip -y是关键参数表示存储符号链接本身而不是链接指向的目标内容。如果不加-y压缩包里的rootlink会变成普通文件内容就是/这个字符串解压后不会成为符号链接。5.2 symlink从哪一步开始绕过路径校验下载接口的逻辑是UPLOAD_PATH . / . $filename然后直接读取文件内容。由于代码没有对filename做realpath校验也没有检查解析后的真实路径是否还在上传目录内所以当路径中包含符号链接时读取操作会直接跟随链接指向的真实路径。这跟目录穿越绕过是两回事。../../../../etc/passwd绕过是依靠..往上跳目录而符号链接绕过的核心是链接文件本身是上传目录内的合法条目但链接的目标可以指向任意目录。如果代码只检查了字符串层面是否包含..或者只检查路径是否在某个目录下符号链接就能绕过这种检查。5.3 完整请求顺序从上传到读取flag在我复现的环境中整个利用链的请求顺序是这样的先通过下载接口的任意文件读取把网站源码和目录结构摸清楚。本地生成一个带phar payload的ZIP文件里面包含触发反序列化的恶意phar文件。用上传功能把这个ZIP传上去服务端自动解压payload落到上传目录。本地生成包含符号链接的ZIP文件再次上传并解压生成指向根目录的rootlink符号链接。通过下载接口请求filenamerootlink/flag直接读取根目录下的flag文件。我复现时发现其实到第5步就能直接读取flag了题目环境把flag放在根目录下的/flag文件中。如果路径不同可以用同样的思路继续探测比如rootlink/var/www/html/flag、rootlink/proc/self/environ等。5.4 flag不在根目录时的扩展思路如果flag不在根目录扩展思路就两条一条是利用任意文件读取顺着符号链接去读各类敏感文件比如/proc/self/environ里的环境变量、/proc/self/cmdline里的启动参数另一条是结合phar反序列化删除某些关键文件触发报错信息泄露更多的目录结构和配置内容。我在测试环境中验证过phar删除任意文件是可靠的。比如我构造了filename指向/var/www/html/index.php的payload触发后页面直接变成了空白说明目标文件确实被删了。这种能力在某些情况下可以用来破坏业务逻辑或者在错误页面中获取更多信息。6. 复现中我踩过的四个坑以及调试思路6.1 路径猜错的时候先看错误信息再动手最初我直接猜/flag路径结果下载接口返回了空内容也没有报错。这里容易让人误判为“文件不存在”但实际上可能是因为权限不够也可能是因为flag根本不在那个路径。后来我学聪明了先故意访问一个显然不存在的路径观察错误回显。如果错误回显里带着物理路径就能顺藤摸瓜。复现时遇到读取结果为空不要急着换路径先把报错页面和HTTP状态码看全。6.2 本地能生成phar但上传后不触发第一次生成phar并上传后怎么触发都没反应。后来发现是因为phar.readonly没有关导致生成了个残缺的phar文件。检查方法很简单在命令行执行php -r var_dump(ini_get(phar.readonly));如果是On就得修改php.ini。另外生成的phar文件要确认一下文件头内容直接用xxd payload.phar | head看看stub是否正常。如果是GIF89a?php __HALT_COMPILER();说明stub没问题。6.3 zip -y丢符号链接Windows用户的坑我在Windows上用压缩软件直接打包符号链接结果解压后符号链接变成了普通文本文件内容就是链接目标路径。这是因为Windows下常用压缩工具默认不保留符号链接属性。正确做法是在Linux环境或WSL下用zip -y命令打包确保ZIP条目类型是符号链接。上传后用下载接口读取一下filenamerootlink/etc/passwd如果能读到passwd内容就说明符号链接生效了如果读不到基本就是ZIP打包方式不对。6.4 环境差异PHP版本和防护组件的影响BUUCTF的题目环境可能不止一套不同环境的PHP版本和防伪随机路径组件可能不同。比如PHP 7.x和PHP 8.x在phar反序列化上的行为就有细微差别有些函数在PHP 8下不再支持某些写法导致触发失败。我的调试方法是先用一个不会产生破坏性后果的payload验证触发点比如删除一个临时文件确认之后再上真正的利用链。如果删除成功就说明phar反序列化链路没有问题剩下的就是路径修正和参数调整。最后再分享一个小经验这类“网盘”题目的核心逻辑本质上就是文件系统操作所以掌握符号链接、目录穿越、phar反序列化这三个技能点特别有用。我在复现过程中最大的收获不是拿到flag的那一刻而是明白了“文件读取漏洞应该优先用来读源码而不是猜flag路径”这个道理。你如果也在刷这道题建议按这个顺序走一遍先抓包梳理功能面再测下载接口的任意文件读取然后顺着源码找反序列化点最后用符号链接闭合路径。整个过程走通一遍比单纯看别人的writeup要扎实得多。
延伸阅读

更多相关文章

2026/10/20 16:10:28

基于7z SDK API的C#压缩包提取器实战

有段时间我几乎天天压在压缩包上,7z、zip、tar、xz格式从各个渠道涌过来,系统自带的解压工具看着能用,可遇到加密、分卷、特殊文件名就开始闹脾气。折腾几次之后,我干脆基于7z SDKAPI动手写了个简易压缩包文件提取器,把…

2026/10/20 16:05:26

MATLAB实现ACO-DRL-RNN混合无人机三维路径规划

做无人机三维路径规划那会儿,我最大的感受是:单一算法撑不起一个真实可用的系统。我最早尝试纯蚁群算法(ACO),在地图上能搜出全局路径,但一旦地形稍有变动,整套规划就要从头再来;后来…

2026/10/20 16:05:26

ICMP隧道原理与icmptunnel实战:内网穿透的合规信道

1. 项目概述:为什么ICMP隧道在内网渗透中不可替代内网渗透系列里,隧道技术是绕不开的核心能力。而当所有常规端口被封死、HTTP/HTTPS流量被深度检测、DNS查询被限频、甚至UDP都被策略拦截时,icmptunnel这个名字就会从工具列表里突然跳出来——…

2026/10/20 18:01:18

微信小程序商城管理系统开发全流程与毕业设计实践指南

1. 项目概述与选题价值 1.1 为什么微信小程序商城成了主流选题 这两年做电子商城相关的课程设计和毕业设计,十个里面至少有六七个同学会选“微信小程序商城”这个方向。我自己带过的模拟项目X里,微信小程序版本占了差不多一大半。原因其实不难理解&…

2026/10/20 18:01:18

微信登录不跳转:从配置到SDK的完整排查指南

做移动端开发这些年,我处理过不少“微信登录不跳转”的工单。用户点一下“微信登录”按钮,如果一直没有反应,或者跳出去进到微信又一瞬间弹回来,很多人第一反应是代码写错了,但排查下来十有八九是配置、签名和 SDK 集成…

2026/10/20 18:01:18

地面辐射不绝缘:芯片软错误与硬故障的成因、量化与防护

地面能躲过辐射吗?先说结论:大气层确实替我们挡住了大部分宇宙射线,但地面上远远算不上辐射绝缘体。高能中子和封装材料里的α粒子,照样能把芯片里一颗存储单元的0改写成1,这种“芯片没坏、数据却错了”的现象&#xf…

2026/10/20 18:01:18

iOS自动化测试进阶:用Cucumber实现BDD驱动与稳定性优化

1. 为什么我的iOS自动化最终选了Cucumber1.1 三条主流路线,我为什么没选纯XCTest开始之前,先把iOS自动化测试的几条路线摆开。我做了几年移动端测试,前后接触过三类方案:纯XCTest/XCUITest方案、Appium搭配各种框架的方案、以及Cu…

2026/10/20 18:01:18

C语言递归入门到实战:调用栈、栈溢出与经典算法详解

递归这个概念,几乎可以称得上是C语言入门的头号“劝退点”。指针是第一个坎,递归就是第二个。我在学习的时候也经历过那种状态:一个函数自己调用自己,代码看着能背下来,到考试题里换个数就懵了,程序一跑深一…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑