ThinkPHP 6.0.1 任意文件写入漏洞

漏洞简介

原理:thinkphp session写入文件功能的参数可控,并且过滤不严格导致了任意文件写入漏洞

影响范围

6.0.0 <= thinkphp < 6.0.1

环境配置

  1. 搭建thinkphp6.0.1版本
  2. 修改配置文件开启session

app\middleware.php

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

app/controller/Index.php

<?php
namespace app\controller;

use app\BaseController;


class Index extends BaseController
{
    public function index()
    {
        $a = isset($_GET['a']) && !empty($_GET['a']) ? $_GET['a'] : '';
        $b = isset($_GET['b']) && !empty($_GET['b']) ? $_GET['b'] : '';
        session($a,$b);
        return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:) </h1><p> ThinkPHP V6<br/><span style="font-size:30px">13载初心不改 - 你值得信赖的PHP框架</span></p></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=64890268" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="eab4b9f840753f8e7"></think>';
    }

    public function hello($name = 'ThinkPHP6')
    {
        return 'hello,' . $name;
    }
}

代码审计

漏洞点

vendor/topthink/framework/src/think/session/driver/File.php

image-20240905205423762

这里是调用链最后写文件的代码,我们接下来分析他的参数是否可控

$path参数

查找writeFile()的用用法,发现只有一个地方调用了

vendor/topthink/framework/src/think/session/driver/File.php

image-20240905205528119

发现writeFile()的参数和write()的参数是对应的,只不过getFileName()函数将原本的$filename变为了sess_$name

再查找write()用法

出现了两个结果,但是根据相关性判断,以下函数更有可能成为调用链

vendor/topthink/framework/src/think/session/Store.php

image-20240905205903454

再跟踪sessionid参数,它是getId()的返回值,这是一个getter方法

vendor/topthink/framework/src/think/session/Store.php

image-20240905210211619

我们找他的setter方法

vendor/topthink/framework/src/think/session/Store.php

image-20240905214313374

这里对id进行了一个简单的校验,仅仅只要求$id===32

再查找setId()的用法

vendor/topthink/framework/src/think/middleware/SessionInit.php

image-20240905210357929

跟踪参数$sessionId

一直跟进发现$sessionId就是cookie中"PHPSESSID"字段的值

$sessionId = $request->cookie($cookieName);

$cookieName   = $this->session->getName();

return $this->name	//getName()

protected $name = 'PHPSESSID';

这样看文件的路径就可控了,而且还没有过滤。

$data参数

跟踪这个参数和$path参数一样

关键点再下面这里

vendor/topthink/framework/src/think/session/Store.php

image-20240905211255544

跟到这里发现,这个$data是一个默认的空数组,它是由一个setter方法来赋值的,但是全局找不到setedata()的调用,最后发现它是由set()方法赋值的

vendor/topthink/framework/src/think/session/Store.php

image-20240905213553138

全局查找set()用法

vendor/topthink/framework/src/helper.php

image-20240905213742127

发现session()函数调用了set()方法,并且第二个参数就会赋值给$data,而session()在入口函数进行了调用

app/controller/Index.php

image-20240905213935251

也就是可控参数b

image-20240905210245117

漏洞复现

poc

?a=1&b=123<?php+phpinfo();?>
cookie: PHPSESSID=/../../../public/12345678901.php

image-20240905215307157

修复方法

vendor/topthink/framework/src/think/session/Store.php

修复前

image-20240905214428743

修复后

image-20240905210245117

给$id多加了一个函数来校验

根据php手册的解释,这个函数会检测他的参数是否仅仅只包含字母和数字

image-20240905215719496

就意味着我们不能传入特殊符号,也就没办法传入php代码

posted @ 2024-09-05 22:02  Litsasuk  阅读(647)  评论(0)    收藏  举报