前言
本着知其然,知其所以然的精神,对thinkphp5 控制器过滤不严导致的RCE漏洞进行了一次审计
POC:
影响版本:thinkphp 5.0.23及以下
环境:phpstorm+xdebug
POC效果:
开始审计
前置知识:
入口文件:Thinkphp5的入口文件位于public目录下的index文件
跟进入口文件,先进行了一些配置加载、设置路由规则的工作
加载完之后进入start.php开始执行
Run方法:
public static function run(Request $request = null)
跟进run方法,首先是自动加载机制autoload加载think\app类
初始化、语言包加载、模块绑定等工作完成后开始获取调度信息dispatch,未设置调度信息则进入routecheck()方法进行url检测
Routecheck方法:
public static function routeCheck($request, array $config)
跟进routecheck()方法,routecheck方法对pathinfo进行分析(tips:thinkphp的pathinfo格式为模块/控制器/操作/[参数名/参数值])
调用path()方法获取到url的pathinfo信息,返回path=” index/think\app/invokefunction”
格式为模块名:index
控制器名:think\app
操作名:invokefuncton
Routecheck()方法载入路由,对比pathinfo以生成调度信息
随后进入路由检测,读取路由缓存内容、导入路由配置,随后进入check()方法根据解析的pathinfo信息与路由进行对比,因路由规则中不存在对应的路由信息,返回$result=fasle,代表路由无效,无调度信息
因为根据路由缓存检测出调度信息无效,所以进入parseURL进行URL的解析进行url的解析以再次获取调度信息
跟进parseURL,parseURL中调用了parseUrlPath来解析url,此时url= “index|think\app|invokefunction”。 parseurlPath将url解析为数组形式,$path:{“index”,”think\app”,”invokefunction”},分别为模块、控制器、操作
ParseURL对parseURLpath返回的数组$path进行模块、控制器、操作的解析,得到结果:模块$module = “index” 控制器$controller=”think\app” 操作 $action = “invokefunction”
随后对获取的信息进行路由封装,得到$route = {“index“,”think\app”,”invokefunction”}
继续跟进,对路由进行记录、检测缓存信息,完成后进入exec()方法
Exec方法:
protected static function exec($dispatch, $config)
跟进exec()方法,exec根据dispatch数组中type字段的值进入module分支,并调用module方法
跟进module方法,module方法首先对模块进行部署、初始化、缓存检查
随后module方法获取模块名index、控制器名think\app、操作名invokefunction
随后分别进入controller()方法、parseName()方法、action()方法设置控制器、操作并载入
设置并加载控制器、操作后通过is_callable()查看invokefunction是否能被调用,若不可调用则抛出404不存在
随后进入invokemethod方法
跟进invokemethod,invokemethod通过反射机制ReflectionMethod调用操作invokefunction,bindParams用于获取绑定参数 args = {“call_user_func_array”,”{system”, {“whoami”}}”}
此时通过反射机制将调用操作指定为invokefunction ,将参数绑定为args = {“call_user_func_array”,”{system”, {“whoami”}}”}
随后进入invokeargs方法,invokeargs通过反射进入invokefunction方法,在此设置反射为call_user_func_array(),绑定参数为system和whoami
再次调用invokeargs()方法,成功调用call_user_func(system(“whoami”))达到远程代码执行的目的
退出module达到命令执行目的
总结
结合此次RCE审计流程来看,漏洞点主要是解析pathinfo的时候并没有对控制器操作进行过滤,导致恶意用户将控制器操作指向invokefunction,再结合call_user_fun_array达到了远程代码任意执行的攻击效果,通过对比thinkphp发布的补丁可以看出,thinkphp通过增加对控制器名的过滤达到修复。