ThinkPHP6 任意文件操作漏洞分析

本贴最后更新于 1520 天前,其中的信息可能已经时异事殊

这个洞出来也有一段时间了,看了创宇的 paper 后觉得蛮简单的,决定自己在本地搭建复现一下,记录一下学习的过程。

 环境准备

apache + thinphp(<=6.0.0 版本 <=6.0.2) + php7 以上

  • ThinkPHP6 起只能使用 composer 来安装,安装 composer、php、apache 的过程我就不赘述了。

执行命令:composer create-project topthink/think tp 6.0.0,其中 tp 是你的文件夹命名,6.0.0 是版本号,6.0.1 也可。

这里说一个问题,我这个时间 Thinkphp 的最新版是 6.0.2,用上面的命令下载下来 framework 是 6.0.2 版本的,我们需要再执行一条命令:composer require topthink/framework:6.0.0:此时就会把将 6.0.0 的版本把 6.0.2 给替换掉

  • 进入 tp 的安装目录,执行 php think run,它会开启一个临时的开发环境的服务器,默认运行在 localhost:8000,打开浏览器访问显示正常即可

漏洞复现在 apache 下进行

 漏洞分析

漏洞影响的版本:top-think/framework 6.x < 6.0.2

  • 官方信息 

ThinkPHP 发布的补丁声称修复了一处由于不安全的 SessionId 导致的任意文件操作漏洞:在开启 Session 的情况下可以导致创建任意文件以及删除任意文件,特定情况下可以 getshell

  • 根据这些信息,我们到官方 GitHub 的 commit 页面找一下相关的提交记录:

github.png

可以看到位于 src/think/session/Store.php 中 212 行在设置 id 时增加了一个函数:ctype_alnum($text)

查一下 PHP 官方手册,这个函数是用来检测输入的 $text 中所有的字符全部是字母和(或者)数字,返回 TRUE 否则返回 FALSE

ctypealnum.png

根据文件目录和更改的函数部分猜测:可能是存储 Session 时导致的文件写入;然后跟进找一下相关的函数,可以看到 vendor/topthink/framework/src/think/session/Store.php:254 的 save()函数,265 行还可以对文件进行删除操作,并且对后端业务逻辑依赖较低

save.png

可以看到设置了 $sessionId,并且调用了一个 write 函数,继续跟进,找到 write()函数 vendor/topthink/framework/src/think/session/driver/File.php:210

write.png

继续跟进,找到 writeFile()函数

writeFile.png

可以看到调用了 file_put_contents() 函数,这里是真正写入文件的操作了

fileputcontents.png

  • 接下来我们反向分析一下,看看能不能找到可控点
  1.  函数 file_put_contents($path,$content,LOCK_EX) 中参数 $path,$content 来源于函数 writeFile($path,$data)

  2. 函数 writeFile($path,$data) 中参数 $path,$data 来源于函数 write(String $sessionID,String $sessiData)

  3. 函数 write(String $sessionID,String $sessiData) 中参数 $sessionID,$sessiData 来源于 save() 中调用了 write(),同时传入的参数 $sessionId 的值是调用 getId() 传入的

综上:文件名来源于 $sessionId

  • 当传入的 id 值长度为 32 并且......etc 时,创建 sessionId,然后进行 gitId()

session.png

  • 接下来找调用 setId() 的地方 vendor/topthink/framework/src/think/middleware/SessionInit.php:46

sessionI.png

其中 cookieName 的值为 PHPSESSID,而 $sessionIdcookie 中名为 PHPSESSID 的值,因此是攻击者可控的,从而导致写入的文件名可控。

但是默认环境下,session 的内容由 vendor/topthink/framework/src/think/session/Store.php:261 的变量 $data 传入:


$data=$this->serialize($this->data);

$data 在默认环境中为空:


/**

     * Session数据

     * @vararray

     */

protected$data=[];

写入的 session 内容是由实际的后端业务逻辑来决定的,所以说只有苛刻的条件下才能写入 webshell。并且一开始就说了需要在环境开启 session 的情况下才可以实现任意文件操作(默认环境不开启 session)

  • 我们在 app\controller\index.php 中增加一些代码后,如下:
<?php

namespaceapp\controller;


usethink\facade\Session;

useapp\BaseController;

  

classIndexextendsBaseController

{

publicfunctionindex()

{

Session::set('name','thinkphp');

return1;

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

}

  

publicfunctionhello($name='ThinkPHP6')

{

return'hello,'.$name;

}

}

  

 忘了说了 thinkphp6 开启 session 的方法:删除 /app/middleware.php 最后一行的注释

kq.png

本地环境复现

很简单,只需要构造 PHPSESSID 的值即可,值为 string&&长度为 32

tp.png

此时查看一下生成的 session,生成的 session 文件保存在 \runtime\session

sessionphp.png

session 里的内容:


a:1:{s:4:"name";s:8:"thinkphp";}

可以看到 session 的内容经过了序列化操作,只要将 session 的内容反序列化即可 getshell

  • 如果要 getshell 的话,后端需要有类似的 Session::Set('name',$_POST['i']) 代码才可以利用

总结:

在复现的过程,也遇到了不少问题:首先 ThinkPHP6 开始不支持 git 了,只能通过 composer 来操作,由于从来没用过它也没经验,一开始安装环境一直下载不到旧版本,后来得到师傅的帮助终于下好了 ThinkPHP6.0.0 的环境,在这里感谢一下师傅 @P1an0 对我的帮助。

这个漏洞其实很简单,就是用户可控变量导致的,也没有对一些数据的过滤等等。需要一定条件才可以利用,也就是开启 session;写 webshell 还要看具体的后端业务逻辑等等。我觉得就这个框架来看其实可以更深入的进行挖掘,希望有大佬可以和我一起探讨学习

参考的 paper:ThinkPHP6 任意文件操作漏洞分析

  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 406 回帖 • 523 关注
  • 漏洞分析
    1 引用 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...