OneThink CMS 缓存漏洞

0x00 前言

最早是2018.7.21 i春秋巅峰极客赛接触到这个漏洞,这也是我第一次认真的搭建环境,分析漏洞。跟着大佬的博客,算是比较清楚的弄懂了这个漏洞。

0x01 漏洞环境

  1. OneThink 1.0
  2. /Temp/Runtime目录可读可写

0x02 漏洞分析

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

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

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

发现TEMP_PATH 默认值为RunTime/Temp目录。
但是 缓存数据文件存储位置为ThinkPHP/Conf/convention.php
mark

下面一步步分析 OneThink 在登陆的时候缓存文件是如何存储的:
登陆时,请求的网页是http://127.0.0.1/onethink/index.php?s=/Home/User/login.html
搜索login,在onethink\Application\Admin\Controller\PublicController.class.php下找到对应方法
其中的D函数是TP中获取model的方法,这里相当于获得了一个MemberModel类的实例,
mark

登陆成功后,调用Member->login方法,跳转到定义:
onethink\Application\Admin\Model\MemberModel.class.php
其中$uid是从数据库中查询出来的用户名对应的用户id。
mark

登陆时,调用autoLogin()函数,继续跟进,找到get_username()函数
mark

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

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

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

我们传入的$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序列化以后存入的值
mark

下面再知道文件名,就完全可控了,看看文件名是怎么定义的。
mark

'onethink_'.md5(md5($name));

最后就是onethink_md5(md5(name)).php

0x03 漏洞利用

现在我们构造payload:

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

在OneThink的注册页面中的注册用户名为:%0aphpinfo();#,但是考虑到有长度限制,可以利用burp抓包改包。
mark

之后用这个用户名登陆,之后访问Temp/Runtime/namemd5.php,即可发现phpinfo得以执行.但是注意一点,就是其$name变量不是usename,而是前面S()函数的参数即sys_active_user_list
mark

访问
http://127.0.0.1/onethink/Runtime/Temp/onethink_d403acece4ebce56a3a4237340fbbe70.php

mark

0x04 漏洞修复

临时解决方案,通过强化缓存的文件名规则,让动态缓存文件难以被反向找到。 修复补丁下载地http://pan.baidu.com/s/1gdAFMSz

补丁修改了最后filename()函数的一句话

  private function filename($name) {
$name = md5(C('DATA_AUTH_KEY').$name); //此处为补丁
// $name = 'onethink_'.md5(md5($name)); //原本代码
if(C('DATA_CACHE_SUBDIR')) {
// 使用子目录
$dir ='';
for($i=0;$i<C('DATA_PATH_LEVEL');$i++) {
$dir .= $name{$i}.'/';
}
if(!is_dir($this->options['temp'].$dir)) {
mkdir($this->options['temp'].$dir,0755,true);
}
$filename = $dir.$this->options['prefix'].$name.'.php';
}else{
$filename = $this->options['prefix'].$name.'.php';
}
return $this->options['temp'].$filename;
}

这样将一个可控的或者说可知的文件名,改成了未知不可控的文件名。

0x05 总结

花了一下午的时间,找漏洞,还是在有博客的帮助下。大牛们挖洞真的也不容易啊。。。

第一次大型CMS的代码审计感觉非常烦躁,好多同名函数,一会就弄混了,希望之后能渐渐游刃有余起来。

0xff 参考链接

OneThink CMS的缓存漏洞的分析
ThinkOX全版本通杀0day

文章作者: V0WKeep3r
文章链接: http://v0w.top/2018/08/02/OneThink%20CMS%E7%9A%84%E7%BC%93%E5%AD%98%E6%BC%8F%E6%B4%9E/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 V0W's Blog
支付宝打赏
微信打赏