0x01 前言
最近的极客巅峰CTF中碰到了这个漏洞的实际利用:
一开始陷入了僵局,还以为是ueditor的文件上传,绕了半天,结果凉凉:
首先访问题目页面,发现是一个OneThink1.0的CMS:
上网站扫描器进行目录扫描和源码泄露扫描,发现存在源码泄露的压缩包www.zip:
0x02 分析
源码下载之后,拖入Seay源码审计工具中分析上述的缓存漏洞是否存在:
首先查看源码中定义的缓存文件路径配置信息,在ThinkPHP/ThinkPHP.php
中对TEMP_PATH
定义为Runtime/Temp
:
在ThinkPHP/Conf/convenion.php
中同时定义了DATA_CACHE_PATH
也为Runtime/Temp
:
在Think/Cache/Driver/File.class.php
中构造函数定义了File缓存类的options['temp']
为DATA_CACHE_PATH
,因此File缓存类的存放路径在Runtime/Temp/
在File缓存类中定义了设置缓存的set函数,在该函数中,一开始定义了$filename
是filename($name)
的返回结果,并且通过file_put_contents
函数将缓存内容写入$filename
路径的文件中,如下:
这个类中的filename
函数定义如下:
其中的FIle类的options['prefix']
为全局设置的空值’’:
另外,由于DATA_CACHE_SUBDIR
配置中设置为false
,因此filename
中会进入else分支,最后$file为空+$name+.php
其中$name
是onethink_+md5(md5($name参数))
所以filename
函数最后返回内容是:
Runtime/Temp/onethink_+md5(md5($name参数))
而set函数中调用时,传入的$name
和$value
参数也来自set函数被调用时的参数,而传入的$value
经过serialize序列化函数进行处理后赋值$data
,并最终作为写入内容
另外注意到:在写入前$data
做了字符拼接,并且前面的字符<?phpn//
格外引人注目,这里要注意//注释符号,后面会解释这一点。
通过调用查询,在ThinkPHP/Mode/Api/functions.php
中定义的缓存函数S中使用了File缓存类的set函数,其中$name
和$value
又是来自调用上层的传参$name
和$value
:
继续回溯查询S被调用的情况,在Appalication/Common/Api/UserApi.class.php
中,发现get_username
函数逻辑中调用了S函数,并且传参"sys_active_user_list"
和$list
。
$list
的内容是通过从数据库查询uid返回的用户信息中,提取第二项拿到的,既
nickname
所以此处调用S函数,会写入临时文件:
onethink_md5(md5("sys_active_user_list"))
既内容为
onethine_d403acece4ebce56a3a4237340fbbe70
内容是用户的nickname。
仍然继续回溯get_username()函数的调用情况,定位到Application/Home/Model/MemberModel.class.php
模型文件中的autoLogin函数和login函数:
分析上面两个函数,login函数将传入的uid进行判断,如果是已注册的用户,则前面的判断分支全部跳过,直接调用了autoLogin函数,并且将数据库查询结果find($uid)
的用户信息返回结果作为$user
进行传参。
在autoLogin函数中将$user
中的uid
一项继续传入上面介绍过的get_username()
函数。
这样,这个逻辑的代码层面已经走过一遍了。在用户登录后的逻辑执行中,将用户的用户名作为缓存项写入了Runtime/Temp
中的临时文件中,并且对用户名没有做任何的过滤和转义。如果缓存文件存在访问权限,可以导致代码执行以及GetShell。
0x03 利用
首先利用该漏洞尝试执行phpinfo。
注册用户%0aphpinfo();#
其中%0a
是让用户名在缓存文件中以新的一行开始(前面分析代码时提到$data
会和前面的<?phpn//
拼接,这里是防止被//
注释),而#注释符则让之后序列化产生的字符失效,防止影响函数执行。
提交注册,通过Burpsuit进行拦截,这里将%0a
进行url编码解码,成为换行符
将数据包放行后,用户注册成功,进入登录界面进行登录:
同样进行抓包拦截,修改%0a后放行:
payload用户登录成功,接下来访问缓存页面
发现成功执行phpinfo()!
在本地搭建同样环境后测试,发现缓存文件中内容如下:
同样的方法向缓存文件中写入代码执行:
%0a$a=$_GET[a];#
%0asystem($a);#
最后,感谢阅读(#^.^#)
审核人:yiwang 编辑:边边