Thinkphp 多语言 RCE

1nhann 2022-12-08 19:31:00

Thinkphp 多语言 RCE

七月份的时候挖到这个洞,卖给了国内一个项目,如今距离修复的 commit 已经过去了将近三个月,在这里公开漏洞细节

影响范围

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

fofa指纹

header="think_lang"

简单描述

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

攻击条件

  1. 开启多语言功能

thinkphp6 ,打开多语言功能

https://www.kancloud.cn/manual/thinkphp6_0/1037637

app/middleware.php

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

thinkphp5 ,打开多语言功能

https://static.kancloud.cn/manual/thinkphp5/118132

config/app.php

application/config.php

'lang_switch_on'         => true

测试环境搭建

这里以 thinkphp 6.0.12 为例

官方下载代码:

https://github.com/top-think/think

root@ubuntu:/var/www/#git clone https://github.com/top-think/think.git think_git
root@ubuntu:/var/www/#cd think_git
root@ubuntu:/var/www/think_git#git checkout v6.0.12

更改 composer.json ,安装 v6.0.12

    "require": {
        "php": ">=7.2.5",
        "topthink/framework": "6.0.12",
        "topthink/think-orm": "^2.0"
    },
root@ubuntu:/var/www/think_git#composer install

然后打开多语言功能:

app/middleware.php

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

启动 docker compose :

version: "3.3"  # optional since v1.27.0
services:
  web:
    image: php:7.4-apache
    ports:
      - "8888:80"
    volumes:
      - /var/www/think_git:/var/www/html

进行攻击

header 、cookie 、query string 都可以作为 payload 的传入点,进行目录穿越 + pearcmd 文件包含,可以写 webshell :

# exp

pearcmd 文件包含这个 trick ,可以参考 p 牛的文章:https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp

漏洞分析、调试

thinkphp 6

调试环境: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 了

thinkphp 5

调试环境:windows ,php7.3,thinkphp5.1.41

打开多语言中间件:

image-20220711145319484

访问

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

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

image-20220711145947527

然后进行文件包含:

image-20220711150159797

修复漏洞

官方已完成修复:

https://github.com/top-think/framework/commit/c4acb8b4001b98a0078eda25840d33e295a7f099

评论

K

kinding 2022-12-10 09:20:57

给大佬点赞

长枪白马 2022-12-16 09:14:50

图片是不是加载不了

secdragon 2022-12-20 15:51:49

@长枪白马 恢复了

B

BigYoung 2023-01-04 13:15:42

不错 不错

1

1nhann

这个人很懒,没有留下任何介绍

随机分类

安全管理 文章:7 篇
APT 文章:6 篇
Java安全 文章:34 篇
企业安全 文章:40 篇
逻辑漏洞 文章:15 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录