OneThink CMS的缓存漏洞的分析
第一次分析漏洞,跟着其他师傅的分析过程自己走了一遍,有的地方不太懂也查清楚了,写一遍加深记忆吧
0x01 漏洞利用条件
- OneThink1.0
- 并且/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
发现$filename
被file_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框架,正好也是一个巩固和提高,虽然是跟着别人的路走的,但是仍然很艰难,可能是第一次分析漏洞吧,这是第一次,但不是最后一次,希望下次能更游刃有余吧