0x1:漏洞复现
首先通过composer创建thinkphp6的框架目录
composer create-project topthink/think think6
创建好了以后,默认是thinkphp6最新版本的,漏洞是已经修复了的,所以我们需要对程序进行降级
编辑程序根目录下的composer.json文件,改成如红框所示
然后执行
compose update
如图所示,即是降级成功
然后是非常重要的一步,很多复现的文章都没有写,就因为这个浪费了我一两个小时的时间.
thinkphp6默认是没有开启session功能的,我们需要在app\middleware.php文件中,取消session中间件的注释,设置为如图
终端运行 php think run 启动调试环境
app\controller\Index.php中添加测试代码
在浏览器f12 console里面执行poc,然后刷新页面
document.cookie="PHPSESSID=aaaaaaaaaaaaaaaaaaaaaaaaaaaa.php";
\runtime\session文件夹下就会生成php文件
如果要利用这个漏洞getshell的话,还要解决两个问题
文件内容可控,而生成的文件里面的内容,就是将session内容的反序列化
如果后端代码里面有类似,这种代码的话就是可以利用的
Session::set('name', $_POST['a']);
2.thinkphp的网站一般会把 /public 设置为网站的根目录,而生成的文件是在/runtime/session 文件夹下面的,默认是访问不到的
但是这个通过,即可绕过
PHPSESSID=/../../../public/aaaaaaaaaaa.php
Python漏洞检测POC
https://github.com/lanyi1998/pochub/blob/master/thinkphp6_file_write.py
0x2:漏洞分析
根据tp官方的提交记录来看,是对setId函数做了修改,那么漏洞一定和这个函数有关,全局搜索一下这个函数
https://github.com/top-think/framework/commit/1bbe75019ce6c8e0101a6ef73706217e406439f2
找到在 vendor/topthink/framework/src/think/middleware/SessionInit.php中
第一步:
从cookie中取出phpsessid的值,赋值给sessionId
第二步:
通过setid函数把session类id设置为,刚才从cookie中取到的值
第三步:
执行session类中的init方法,本来以为写入操作是在这个函数里面进行的,但是debug以后发现不是的
看thinkphp文档
tp6会在请求结束的时候进行session写入
vendor\topthink\framework\src\think\Http.php
会调用vendor\topthink\framework\src\think\middleware\SessionInit.php下的end方法
接下来调用vendor\topthink\framework\src\think\session\Store.php中的save方法
跟进到红框所示的write方法,会执行vendor\topthink\framework\src\think\session\driver\File.php中
通过getFileName函数,完成session文件名的拼接,然后再调用writeFile函数
session文件写入流程完成!
0x3:漏洞修复
查看补丁是对id增加了一个ctype_alnum函数,查一下文档
如果传进来的id里面含有如 . 这样的,就会使用来随机生成一段id,而不会实用传进来的ID
md5(microtime(true) . session_create_id()