漏洞简介
think\Session中的setId方法未对PHPSESSID进行校验,因此造成了任意文件写漏洞,漏洞存在版本:ThinkPHPv6.0.0-v6.0.1
配置
1 | composer create-project topthink/think tp6 |
在app/controller/Index.php
下修改代码
1 |
|
在app/middleware.php
开启session功能(默认开启)
官网关于session的配置:https://www.kancloud.cn/manual/thinkphp6_0/1037635
1 |
|
正常访问页面如下
漏洞演示
cookie设置为
1 | PHPSESSID=/../../../public/shell123456.php |
需要32位的Cookie,然后通过c传参设置cookie中的demo
1 | /?c= phpinfo(); |
访问shell123456.php
漏洞分析
在框架运行前,先进入src/think/middleware/SessionInit.php
的handle
方法初始化session
1 | public function handle($request, Closure $next) |
$sessionId
为cookie传入的/../../../public/shell123456.php
,调用think\Session
的setId方法设置$this->id
可以看到需要$id
的长度为32,才能将$this->id
设置为PHPSESSID
中的值
由于第一次初始化,此时session文件为空,会将$this->init
设置为true
然后继续执行handle
代码
在$next处会进行类的反射调用,处理用户的请求,进入到Index.php
跟进session方法初始化session
调用ThinkPHP官方文档中的session设置函数Session::set设置session,设置好session后回到SessionInit
的handle函数处理剩下的代码
这里调用了think\Cookie
方法设置Cookie为$this->id
也就是我们传入的PHPSESSID
=>/../../../public/shell123456.php
在public/index.php
中调用end方法处理响应后的信息
在think/Http.php
中的$this->app->middleware->end
处通过反射获取think\Middleware
类执行end方法
实例化一个SessionInit
方法调用end
方法
调用think/session/Store.php
的save函数准备将session信息写入文件
调用$this->handler
即think\session\driver\File
的write函数处理$sessionId=>/../../../public/shell123456.php
和$data=>a:1:{s:4:"demo";s:18:"<?php phpinfo();?>";}
通过$this->getFileName
获取$sessionId
中的文件名
1 | protected function getFileName(string $name, bool $auto = false): string |
直接将sess和$sessionId
拼接到一起形成sess_/../../../public/shell123456.php
为$filename
,然后用dirname
截取$dir
创建目录sess_
最后在writeFile函数中写入shell文件,分析到这里就结束了
漏洞修复
在src/think/session/Store.php
处的setId使用了ctype_alnum方法确保传入的session文件名是字母和数字