ThinkPHP 6.0 任意文件写入

admin 2024年8月18日00:17:35评论10 views字数 2403阅读8分0秒阅读模式

概述

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

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

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

环境搭建

composer 创建项目。

composer create-project --prefer-dist topthink/think=6.0.0 thinkphp6.0.0

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

class Index extends BaseController {
public function index() {
session('test', input('j'));
}

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

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

复现

ThinkPHP 6.0 任意文件写入

ThinkPHP 6.0 任意文件写入

ThinkPHP 6.0 任意文件写入

分析

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

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

# src/think/session/Store.php
$this->id = is_string($id) && strlen($id) === 32 ? $id : md5(microtime(true) . session_create_id());

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

// 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);
}

ThinkPHP 6.0 任意文件写入

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

// 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($sessionId, $data); 的具体实现。

// 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

FROM:wywwzjj

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月18日00:17:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ThinkPHP 6.0 任意文件写入https://cn-sec.com/archives/3075501.html

发表评论

匿名网友 填写信息