CTF Web安全

ThinkPHP6 任意文件操作漏洞分析

Posted on 2020-03-01,4 min read

当时i春秋的CTF有一题thinkphp6 任意文件写入getshell。现在来分析一下

漏洞概述
在 ThinkPHP 6.0.0 以及 6.0.1 两个版本中,如果服务端在全局中间件定义文件中开启了 Session 初始化,并且攻击者可控任意一个 session 的值,就可利用写入 session 文件的功能做到写入任意文件 getshell 。

安装Thinkphp6:
composer create-project topthink/think tp
修改配置:
修改app/middleware.php为如下配置

并删除vendor/topthink/framework/src/think/session/Store.php中setid的补丁

既然官方在这里打了补丁。那么就从这为入口点分析
补丁即对sessionid只能为数字字母。那么漏洞应该就是在sessionid上
先看setId被谁调用了

if ($sessionId) {
    $this->session->setId($sessionId);
}
#只有满足$sessionId才能进入setid函数

if ($varSessionId && $request->request($varSessionId)) {
    $sessionId = $request->request($varSessionId);
    #如果在配置中。自定义了SESSID那就取自定义的
} else {
    $sessionId = $request->cookie($cookieName);
    #否则。就默认PHPSESSID
}
#这里sessionId可以由cookieName来。继续看
 $cookieName   = $this->session->getName();


可以看到$sessionId=$request->cookie('PHPSESSID')
sessionId其实就是等于PHPSESSID的值。现在$this->id已经知道是PHPSESSID的值。
在setId函数下方有个getId函数

看看哪里调用了
vendor/topthink/framework/src/think/session/Store.php中发现save()函数

根据函数的注释。这个函数用来保存session数据

$sessionId为id名。然后session数据为$data
如果$data不为空。就序列化为字符串。调用write函数

我们继续看write函数。
vendor/topthink/framework/src/think/session/driver/File.php中发现write函数

$filename为sessionid名
$data为sessionid的值
最后会通过writeFile函数写入。

继续搜writeFile函数
在同文件的170行发现此函数。就是个file_put_contents

简单概括下。这个漏洞。就是讲sessionid作为文件名。session内容作为文件内容写入。
如果我们能控制sessionid和文件内容。即可任意文件写入

   public function setId($id = null): void
    {
        $this->id = is_string($id) && strlen($id) === 32) ? $id : md5(microtime(true) . session_create_id());
    }

在setid时有个判断。sessionid必须是长度为32的字符串。那么我们构造
PHPSESSID=1234567890123456789012345678.php
就可以写入一个名为sess_1234567890123456789012345678.php的文件。只需要一个文件内容可控即可

接下来具体分析下i春秋那题
首先下载源码。

正常来说。index.php应该是在public目录下作为程序入口。web目录也是public
可这个直接在根目录下。那么app。config。runtime这些目录只要有权限都可访问
结合thinkphp6任意文件。那么我们只要写入文件。可以直接访问runtime/session。就可拿到我们的webshell

OK。从程序入口文件入手

HTTP请求结束后,会调用end方法
接着会继续执行下去。又是一个end方法

进去看。末尾***还是end方法。

到这里调用了save方法

而save方法。就是我们上述分析的漏洞利用点
PHPSESSID我们可控。改为32长度以.php结尾的字符串即可
接下来找session内容可控处
在app/home/controller/Member.php中。我们找到了search方法


这里。我们搜索内容可控。并且会放在session内容中。
这里。我们在登陆时修改phpsession。为xxxxxxxxxxxxxxxxx.php(总长32)

登陆成功。会给10c26cc264e03957eb7bba1f6d75.php这个PHPSESSID赋一个UID的值表示登陆
登陆后搜索<?php phpinfo();?>
此时PHPSESSID还是10c26cc264e03957eb7bba1f6d75.php带有登陆标志。可以通过search的判断
访问/runtime/session/sess_10c26cc264e03957eb7bba1f6d75.php
可以看到phpinfo的界面

下一篇: [V&N2020 公开赛]HappyCTFd/CHECKIN/TimeTravel→