项目介绍
ThinkPHP 是一个快速、简单的面向对象的轻量级 PHP 开发框架,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。
根据目前FOFA系统最新数据(一年内数据),显示全球范围内(header="think_lang")共有 73,302 个相关服务对外开放。中国使用数量最多,共有44,788 个;美国第二,共有 12,295;中国香港特别行政区第三,共有5,009 个;新加坡第四,共有 896 个;日本第五,共有 246 个。
项目地址
https://www.thinkphp.cn/
漏洞概述
如果 Thinkphp 程序开启了多语言功能,攻击者可以通过 get、header、cookie 等位置传入参数,实现目录穿越+文件包含,通过 pearcmd 文件包含这个 trick 即可实现 RCE。
默认情况下,系统只会加载默认语言包,如果需要多语言自动侦测及自动切换,需要在全局的中间件定义文件中添加中间件定义。因此只有启用多语言并且使用存在漏洞的版本时才存在漏洞。
影响版本
v6.0.1 < Thinkphp < v6.0.13
Thinkphp v5.0.x
Thinkphp v5.1.x
环境搭建
docker run -it -d -p 80:80 vulfocus/thinkphp:6.0.12
漏洞复现
漏洞分析
这里以thinkphp 6为例进行分析,thinkphp 5类似。
Thinkphp的中间件默认会调用handle函数,因此直接在LoadLangPack.php的handle函数中下断点
在detect函数中会从request中提取多个参数
查看config可知,detect就是从请求的不同位置中提取lang等参数
默认情况下,allow_lang_list 这个配置为空,因此从lang参数中拿到的变量没有过滤直接传给了$this->range并返回该值
得到lang值之后,会调用switchLangSet函数加载对应的语言。在该函数中会先根据$langset拼接路径(这里我们就可以目录穿越),然后调用load进行加载
在load函数中调用了parse,处理上面拼接过的文件
在parse中可以看到,当后缀为php时直接include
因此攻击者可以通过目录穿越实现任意 php 文件的包含
分析了官方的修复方式,发现官方通过两个commit修复了此漏洞,核心改动如下,即判断了$langSet是否满足条件否则获取默认值:
修复方式
官方已经发布最新版,请升级到最新版
参考链接
删除废弃方法 优化多语言检测 · top-think/framework@c4acb8b · GitHub
调整 · top-think/framework@4c328dc · GitHub
Thinkphp 多语言 RCE - 跳跳糖
Docker PHP裸文件本地包含综述 | 离别歌
https://nosec.org/home/detail/5050.html