ThinkPHP v5.0.22 RCE分析
前言
距离上次php审计是在ThinkPHP v5.0.24反序列化漏洞分析,这篇笔记本来在很久之前应该就要写的,但是一直对tp路由还是不够熟悉,个人觉得难度要比反序列化高一些,拖了这么久今天做个了结。
环境搭建
使用环境:Apache + PHP 7.0.9
下载具有漏洞环境的ThinkPHP 5.0.22
https://github.com/top-think/think/releases/tag/v5.0.22
下载后通过composer安装依赖项:
composer install
或者可以通过composer直接拉取源码:
composer create-project topthink/think tp 5.0.22
查看thinkphp/library/think/App.php
文件555行,注释如下3行代码,这是官方对5.0.22版本漏洞的修复
poc:/think-5.0.22/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
漏洞分析
分析工具:PhpStorm + Xdebug
通过poc和动态调试的方式进行跟踪分析,入口文件public/index.php
加载thinkphp/start.php
然后进入执行App::run
,进到thinkphp/library/think/App.php
,run
方法中对路由处理的核心代码如下:
打上断点进行debug:
$dispatch
为空,进行路由检测
进到routeCheck
方法:
$path
开头获取到路由,$depr
为默认路由分隔符/
接下来的这一块主要是通过配置文件查看路由的开启情况以及读取路由缓存和导入路由配置信息这些,继续往下看
这一块依旧是路由检测,可以进到Route::check
看一看,通过注释可以快速理解代码作用,内部经过一系列检测后,返回self::checkRoute
方法返回值false
然后就是最后一块代码,通过Route::parseUrl
进行路由的解析
先通过str_replace
替换路由为index|think\app|invokefunction
,然后进入parseUrlPath
再将|
替换成/
然后提取模块/控制器/操作
返回parseUrl
方法继续往下看,然后通过解析控制器、解析操作,通过模块/控制器/操作
封装到$route
中然后返回
然后再回到App::run
中继续往下看,$dispatch
接收返回值如下:
之后再139行,进入到exec
方法
这里$dispatch['type']
为module
,然后进入了module
方法
再553行获取控制器名,官方补丁通过正则对$controller
进行了过滤
然后继续获取操作名,然后从590行is_callable([$instance, $action])
验证控制器中是否存在该操作,如果存在通过反射执行该操作。
在611行执行通过invokeMethod
方法进入invokeFunction
方法,这里传入的$vars
为空
进入到invokeMethod
方法,339行获取参数
然后在invokeMethod
最后一行将参数传入invokeFunction
方法
然后进到invokeFunction
方法,又通过反射执行call_user_func_array
函数,传入参数args
最后成功执行call_user_func_array("system", array("whoami"))
总结
这篇rce分析真的挺折磨我的,虽然说大致上明白了这个漏洞的成因,但是我觉得可能由于自己对框架的不熟悉,在debug中有很多的细节其实不太能看太懂,代码较多只能靠注释去尝试理解。这个漏洞对于有开发能力的朋友应该很容易理解,可能真的需要花一定的时间去了解一下Web框架的设计模式,包括其中涉及的反射提前学习过也会加快理解。
还差的太多,需要继续加油😥