tp6<6.0.1 任意文件写入与删除

1.环境搭建:

php7版本以上,用composer直接搭建:

1
composer create-project topthink/think tp6 6.0.*-dev

用thinkphp内置命令测试:

1
./think run --host=0.0.0.0 --port=8000

开启thinkphp的session选项:
/app/middleware.php

upload successful

修改session后端逻辑:

upload successful

总结:

1
2
在目标环境为Windows且开启session的情况下,容易遭受任意文件删除攻击。
在目标环境开启session且写入的session可控的情况下,容易遭受任意文件写入攻击。

2.漏洞跟踪

构造poc如下:

upload successful

upload successful

1
2
3
4
5
6
7
8
9
10
GET /?a=<%3fphp+phpinfo()%3b%3f> HTTP/1.1
Host: 192.168.1.103:8000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=/../../.././././././public/2.php
Connection: close

根据官方github的commit:

upload successful

确定官方的修复方式是通过限制phpsessionid为数字和字母来进行修复,确定是跟session相关。
跟踪session初始化的文件/vendor/topthink/framework/src/think/middleware/SessionInit.php:

upload successful

跟进setId函数,在thinkphp的session文件/vendor/topthink/framework/src/think/session/Store.php:

upload successful

upload successful

在setId中,规定了phpsessionid的值长度为32位并且没有任何过滤,然后直接赋值给store::id。
接下来,调用init方法对session进行初始化(也就是对session进行反序列化):

upload successful

下面还有个end函数,跟进save方法:

upload successful

upload successful

跟进write函数,很明显存在文件操作的流程。

upload successful

跟进getFileName,发现会以sess_为前缀创建文件,并且创建的文件以及文件夹权限为755。

upload successful

再跟进writefile函数,就是用file_put_contents写文件。从这里就可以看出我们创建的session文件前缀是以sess开头,并且内容就为我们创建session时传入的内容,文件名即我们的phpsessionid的值。

upload successful

然后发送设置的session(即获取到的phpsessionid)到服务端。接下来跟进$response,服务器会将之前获得的session返回给用户。那么问题就来了,在这session初始化的过程中,我们没有看到有文件写入的过程,包括各厂商的预警或者分析里面都没有明确说哪里有文件写入,但是这个漏洞确实是跟文件操作相关的,所以我们现在就需要找到到底是哪一步会存在save,即文件写入操作。

像这种只能定位到函数点,没有具体的实现功能的地方我们要想得知完整的调用栈,一般来说有2种方法。顺着我们的后端业务逻辑下断点一步一步跟进顺推,或者根据函数寻找上一层调用来逆推。这里,我们采用前一种方法,即顺推,接下来跟进我们构造的后端业务逻辑所调用的函数session:

upload successful

最后会用Session::set这个静态函数,跟进该函数:

upload successful

再跟进Arr::set,其实最后返回的就是session的数组然后赋值给$this->data。

upload successful

熟悉tp这种mvc框架的都知道,thinkphp的入口文件为/public/index.php,我们很多时候发起的请求都是由/public/index.php自己寻找路由进行逻辑处理,并返回数据的。

upload successful

其中,在end函数中,会存在执行中间件和写入的操作。

upload successful

再跟进end函数,其中$middleware其实就是SessionInit::class,然后通过make方法执行中间件类的相关方法并返回一个对象,在上面我们知道,SessionInit:class类中就存在save方法,因此文件写入的过程中其实是发生在end函数中,也就是一个结束调度的过程。

upload successful

upload successful

整理一下调用的堆栈:

1
2
3
4
5
6
7
Store.php:256, think\session\Store->save()
Manager.php:174, think\Session->__call()
SessionInit.php:78, think\Session->save()
SessionInit.php:78, think\middleware\SessionInit->end()
Middleware.php:165, think\Middleware->end()
Http.php:279, think\Http->end()
index.php:24, {main}()

know it then enjoy it~

打赏
  • © 2020 sommous

老板,来杯卡布奇诺~

支付宝
微信