技术改变世界,文化改变人心

OneThink CMS的缓存漏洞的分析

第一次分析漏洞,跟着其他师傅的分析过程自己走了一遍,有的地方不太懂也查清楚了,写一遍加深记忆吧

0x01 漏洞利用条件

  1. OneThink1.0
  2. 并且/Temp/Runtime目录可读可写

0x02 漏洞分析

因为TP对缓存设计逻辑的漏洞,以及缓存文件名可猜测的原因,导致了这个漏洞

先看一下TP的缓存文件的配置:

TP中一些系统常量的定义都在ThinkPHP/ThinkPHP.php中定义,缓存路径也在这里:

可以看到其中的TEMP_PATH默认值为Runtime/Temp目录

而缓存文件存储的位置在ThinkPHP/Conf/convention.php

我们开始从登陆开始一步步分析OneThink在登陆的时候缓存文件是如何存储的:

在抓包以后,我们发现请求的地址为/home/user/login.html,那么跟踪这个请求,我们可以定位到Home模块的UserController控制器中的login方法:

其中在登陆成功以后调用了$Member中的login方法,传入的$uid是从数据库中查询出来的用户名对应的用户id

其中的D函数是TP中获取model的方法,这里相当于获得了一个MemberModel类的实例,我们进入MemberModel中查看一下其中的login函数

其中的$user是从$uid中拿到的,也就是用户名

在登陆用户的时候调用了autoLogin函数,进入这个函数里面查看

里面调用了get_username函数,继续跟入

终于找到了对于用户名的缓存操作

在第一次登陆的时候是没有缓存的,if条件应该直接进入else部分,又因为$list是从$uid所在行的第二行拿到的,所以应该是用户名,所以在下面调用S方法缓存数据的时候传入的$list我们是可控的

再进入S函数,查看一下这个函数的具体设计

我们传入的$list就是S函数中的value参数,分析if条件的话可以知道程序直接进入第二个elseif,初始化$cache,关键在最后的set函数,set函数中的$value仍然是我们可控的,进入set函数中,set函数在ThinkPHP/Library/Think/Cache/Driver/File.class.php

发现$filenamefile_put_contents直接调用,如果不开启数据压缩的话,$data则是我们控制的$value序列化以后存入的值

现在我们可以控制文件中的一部分内容,知道了文件存储的目录,如果我们知道文件名就好了,那么我们进入filename函数里面看一下缓存文件的文件名是怎么定义的

因为一般没有开启DATA_CACHE_SUBDIR,所以判断直接跳到else部分,而options[‘prefix’]是空的,所以最后的文件名为md5($name).php

现在我们构造payload:

用户名注册为%0aphpinfo();#这样在存储缓存文件的时候就可以写入webshell,#注释了序列化的剩余部分,前面的%0a的作用则是为了不让$data<?php\n//".sprintf('%012d',$expire).$check.$data."\n?>的’//‘注释攻击代码

0x03 漏洞利用

在OneThink的注册页面中的注册用户名为:%0aphpinfo();#,并且用burp抓包将%0a解码

之后用这个用户名登陆,同样抓包将%0a解码,之后访问Temp/Runtime/用户名md5.php,即可发现phpinfo得以执行

查看缓存文件的内容,可以发现payload的原理:

0x04 总结

这是第一次这么系统的分析一个漏洞,可以说学到了很多,最近也在学TP框架,正好也是一个巩固和提高,虽然是跟着别人的路走的,但是仍然很艰难,可能是第一次分析漏洞吧,这是第一次,但不是最后一次,希望下次能更游刃有余吧