前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThinkPHP 6.0 任意文件写入

ThinkPHP 6.0 任意文件写入

作者头像
wywwzjj
发布2023-05-09 14:26:28
9680
发布2023-05-09 14:26:28
举报
文章被收录于专栏:wywwzjj 的技术博客

概述

2020 年 1 月 10 日,ThinkPHP 团队发布一个补丁更新,修复了一处由不安全的 SessionId 导致的任意文件操作漏洞。

该漏洞允许攻击者在目标环境启用 session 的条件下创建任意文件以及删除任意文件,在特定情况下可 getshell。

具体受影响版本为 ThinkPHP 6.0.0 - 6.0.1。

环境搭建

composer 创建项目。

代码语言:javascript
复制
composer create-project --prefer-dist topthink/think=6.0.0 thinkphp6.0.0

在 app/controller/Index.php 中加一行代码,使 session 内容可控,方便漏洞复现。

代码语言:javascript
复制
class Index extends BaseController {
    public function index() {
        session('test', input('j'));
    }

PS:TP 6 默认没开启 session,手动开下,在 app/middleware.php 取消注释即可。

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

复现

image.png
image.png
image.png
image.png
image.png
image.png

分析

https://github.com/top-think/framework/commit/1bbe75019ce6c8e0101a6ef73706217e406439f2

如果传入的 id 长度为 32 即可控。TP 6.0.2 加了个条件,用 ctype_alnum 检测了下 ​id,只能是字母或数字。

代码语言:javascript
复制
# src/think/session/Store.php
$this->id = is_string($id) && strlen($id) === 32 ? $id : md5(microtime(true) . session_create_id());

handle 函数将 cookie 中的 PHPSESSID 对应的值设为 sessionId。

代码语言:javascript
复制
// middleware/SessionInit.php
public function handle($request, Closure $next) {
    // Session初始化
    $varSessionId = $this->app->config->get('session.var_session_id');
    $cookieName   = $this->session->getName();

    if ($varSessionId && $request->request($varSessionId)) {
        $sessionId = $request->request($varSessionId);
    } else {
        $sessionId = $request->cookie($cookieName);
    }

    if ($sessionId) {
        $this->session->setId($sessionId);
    }
image.png
image.png

剩下的文件处理其实就是 session 本身的处理了,比如 $_SESSION 数组被序列化后写入文件保存以及清除。

代码语言:javascript
复制
// src/think/session/Store.php
/**
* 保存session数据
* @access public
* @return void
*/
public function save(): void {
    $this->clearFlashData();
    $sessionId = $this->getId();

    if (!empty($this->data)) {
        $data = $this->serialize($this->data);
        $this->handler->write($sessionId, $data);
    } else {
        // data 为空就进行删除
        $this->handler->delete($sessionId);
    }

    $this->init = false;
}

跟进 this->handler->write(

代码语言:javascript
复制
// session/driver/File.php
public function write(string $sessID, string $sessData): bool {
    $filename = $this->getFileName($sessID, true);
    $data     = $sessData;

    if ($this->config['data_compress'] && function_exists('gzcompress')) {
        //数据压缩
        $data = gzcompress($data, 3);
    }

    return $this->writeFile($filename, $data);
}

// 这里就落实到 file_put_contents() 了
protected function writeFile($path, $content): bool {
    return (bool) file_put_contents($path, $content, LOCK_EX);
}

总结

总的来说还是比较鸡肋,需要能控制 session,直接打不了。

所以要与具体的业务结合,寻找 session 的输入点,比如某些系统将用户名直接存入 session 中。

另外,那个删除点就更难控制了,那也是 TP 清除 session 的正常功能,所以能删的文件必须以 sess_ 开头。

参考

https://mp.weixin.qq.com/s/UPu6cE20l24T6fkYOlSUJw

https://mochazz.github.io/2020/01/14/ThinkPHP6.0%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E5%86%99

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/01/17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 环境搭建
  • 复现
  • 分析
  • 总结
  • 参考
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档