前言:

审计代码能力太弱了,每次一看到审计代码的题目就不知道要如何进行,恰好做Web题的时候发现了一个有关ThinkPHP6.0漏洞的题目,趁这个机会也练习一下。

0x00:漏洞介绍

2020年1月10日,ThinkPHP团队发布一个补丁更新,修复了一处由不安全的SessionId导致的任意文件操作漏洞。该漏洞允许攻击者在目标环境启用session的条件下创建任意文件以及删除任意文件,在特定情况下还可以getshell。

具体受影响版本为ThinkPHP6.0.0-6.0.1

0x01:环境准备

Windows:Apache + Thinkphp(6.0.0) + php7.2.10

先安装composer工具,我这里使用的是phpstudy,在php目录找到一个php7版本的即可,安装好之后

composer create-project topthink/think tp 6.0.0
#tp是文件夹命名
#6.0.0是版本号

进入安装目录,执行命令

php think run
#会开启一个临时的开发环境的服务器,默认运行在localhost:8000

0x02:漏洞分析

根据官方Github上提供的

修改的文件是

src/think/session/Store.php

先来看一下,添加ctype_alnum这个函数有什么作用

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

通过官方的修复可以推出来漏洞的成因,主要原因应该是出现在Session中,那下面就来分析一下Session的相关代码

/vendor/topthink/framework/src/think/session/Store.php
#TP6存储Session文件的目录

漏洞介绍中说到该漏洞允许攻击者在目标环境启用session的条件下可以删除任意文件创建任意文件,那就先定位一下关键词delete,既然是任意文件删除,那么参数就一定是用户可控的。

delete关键字的一共有三处,可以推测在函数265行的位置很可能是该漏洞的形成位置,因为涉及到了SessionId。而且漏洞介绍中也说了可以任意创建文件,在delete关键字上面就有一个write函数且包含有SessionId

跟踪一下这个write函数,在全局搜索函数中找到了write()delete()函数的详细定义

vendor/topthink/framework/src/think/session/driver/File.php

发现有一个writeFile函数,再跟进一下看看

发现了函数file_put_contents(),这里进行了写入文件操作,根据所含的参数进行反向分析一下,看能否找到可控点:

file_put_contents函数中的两个参数来源于writeFile($path, $content)函数中

writeFile这个函数中的参数又是从write()函数的两个参数$sessID、$sessData中获取到的

全局搜索一下参数write函数

发现很可疑的$sessionID$data,进去仔细查看一下,这里就可看出来了,这两个参数来源是来自Save()函数调用write()函数

而且$sessionID是从getId()函数得到了,可以直接搜这个函数,也可以进行猜测,既然有getId函数,就一定有setId()

兜兜转转又回到了官方修补的地方,再继续审计

$this->id = is_string($id) && strlen($id) === 32 && ctype_alnum($id) ? $id : md5(microtime(true) . session_create_id());

当传入的参数$id满足32位的时候,便会将该值设置为$this->id。查找一下setId被调用的地方

vendor/topthink/framework/src/think/middleware/SessionInit.php

简单分析一下,变量cookieName的值来源于getName函数

找一下属性name

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

既然写入文件名可控,那么写入的内容是否可控那?在默认环境下,session的内容vendor/topthink/framework/src/think/session/Store.php:261的变量$data传入

找一下$data,发现默认为空值,默认环境是不开启session的

写入的内容就是创建session使用的内容。但是session的创建是由实际的后端业务逻辑来决定的,而默认环境下并没有创建session。因此,默认环境下无法做到任意文件写入。

参考了Is4b3lla3师傅的方法,尝试复现复现

app\controller\index.php中修改下代码,如下:

<?php

namespace app\controller;

use think\facade\Session;
use app\BaseController;

class Index extends BaseController
{
    public function index()
    {
        Session::set('name','thinkphp');
        return 'lemon';
        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>';
    }

    public function hello($name = '

相关文章: