前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Thinkphp 6 小于 6.0.2 任意文件创建覆盖漏洞分析

Thinkphp 6 小于 6.0.2 任意文件创建覆盖漏洞分析

作者头像
信安之路
发布2020-02-24 13:01:41
1.8K0
发布2020-02-24 13:01:41
举报
文章被收录于专栏:信安之路信安之路

本文作者:1x2Bytes(信安之路红蓝对抗小组成员)

6.0.0 中有两个版本存在该漏洞, dev 版本只能覆盖任意位置的文件,6.0.0-1 则可以在特定的情况下控制写入的内容实现 getshell,看到一些师傅的 blog 的文章使用 composer 下载的源码, Thinkphp6 也确实开始使用 composer 的方式进行安装但是我使用 composer 方式下载的源码无法复现,猜测进行了修复,于是在网上找一键安装包,找了半天找到一个 11 月份的版本遂复现成功.`

具体漏洞位置:

vendor\topthink\framework\src\think\session\Store.php 文件254行开始

代码语言:javascript
复制
public function save(): void
{
        $this->clearFlashData();
        $sessionId = $this->getId();
        if (!empty($this->data)) {
            $data = $this->serialize($this->data);
            $this->handler->write($sessionId, $data);
        } else {
            $this->handler->delete($sessionId);
        }
        $this->init = false;
    }

这里 $this->handler->write($sessionId, $data)是漏洞的关键位置,handler的值我们从文件开头 53 行的__construct方法中可以看到 handler 是 SessionHandlerInterface 接口

我们搜索 SessionHandlerInterface

分别发现 File 类与 Cache 类都实现了该接口, 查看了 Cache 的 write 方法,并没有进行文件写入的操作,于是分析 File 中的 write 方法,看注释应该是跟 Session 操作相关,在文件vendor\topthink\framework\src\think\session\driver\File.php 的 210 行

$filename变量是从 getFileName 方法中获取,传入的值为 $sessID, 跟进该方法,在 File 文件的 117 行

代码语言:javascript
复制
 protected function getFileName(string $name, bool $auto = false): string
{
        if ($this->config['prefix']) {
            // 使用子目录
            $name = $this->config['prefix'] . DIRECTORY_SEPARATOR . 'sess_' . $name;
        } else {
            $name = 'sess_' . $name;
        }
        $filename = $this->config['path'] . $name;
        $dir      = dirname($filename);
        if ($auto && !is_dir($dir)) {
            try {
                mkdir($dir, 0755, true);
            } catch (\Exception $e) {
                // 创建失败
            }
        }
        return $filename;
    }

这里判断是否有配置 session 文件的前缀,配置文件在config/session.php,如果存在配置则拼接到路径的最后并在 $name 前加上字符串sess_,不存在则直接拼接sess_前缀后返回文件名,最后 write 方法进行了 writeFile 操作,跟进 writeFile 方法,在文件 170 行进入 file_put_contents 操作,其中的文件名和内容我们都可控,我们下一步要查看如何控制我们写入的值和文件名

回到前面的 save 方法,传入的$sessionId变量是 getId 方法获取的,查看 getId 方法

该方法返回 id 的值,该值已经在 setId 方法中进行设置,于是查看 119 行的 setld 方法

代码语言:javascript
复制
 public function setId($id = null): void
{
        $this->id = is_string($id) && strlen($id) === 32 ? $id : md5(microtime(true) . session_create_id());
    }

这里对 $id 的值进行了判断长度是否为 32 位,所以构造 payload 的时候要注意长度为 32

查找使用 setId 方法的文件,在vendor\topthink\framework\src\think\middleware\SessionInit.php46 行

$varSessionId 变量的值从配置中获取session.var_session_id的值,因为 session.var_session_id默认是空 ,所以进入另一分支$sessionId变量的值由$request->cookie($cookieName)获取, $cookieName 由 $this->session->getName() 获取,查看 getName 方法

返回的值为 name,查看 name 变量的值在 Store 文件 36 行已经赋值,为 PHPSESSID

复现的时候要在 app/middleware.php 文件中开启即去除注释 \think\middleware\SessionInit::class然后在控制器中使用 Thinkphp 的 session 方法设定值,在 Index 控制器中修改 index 方法

代码语言:javascript
复制
   public function index()
{
    if($_GET['code']){
        session('test', $_GET['code']);
        return 'ThinkPHP V6.0.0';
    }
    }

搭建好后使用以下 Payload:

代码语言:javascript
复制
../../../../testgetshellvuln.php //在根目录下写入文件
../../../../public/shellvuln.php //写入public

成功 getshell

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 信安之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档