Thinkphp <= 5.0.10 缓存getshell复现

Posted litlife

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Thinkphp <= 5.0.10 缓存getshell复现相关的知识,希望对你有一定的参考价值。

Thinkphp <= 5.0.10 缓存getshell复现

0x01 poc

首先看缓存函数的使用场景

技术图片

然后会生成以下缓存文件

技术图片

可以看到,字符串abc直接存储到以php结尾的缓存文件中。尝试使用\\n换行getshell

技术图片

技术图片

语法有错,注释一下后面的垃圾字符,成功getshell。

技术图片

技术图片

技术图片

0x02 跟踪源码

技术图片

首先跟进18行的Cache::set()函数

技术图片

跟进self::init()

技术图片

self::$handler此时为null,进入true块,由于上面调用的是self::init(),没有参数,故67行条件不满足。

69行,查看配置cache.type的值,发现默认为File,

技术图片

故此条件也不满足,进入72行。此处以上图的cache数组作为参数,调用了self::connect()。跟进connect方法

技术图片

这里通过一系列判断,根据cache.type的值,找到cache驱动为File,对应44行的think\\cache\\driver\\File类。然后在51行进行实例化,并return。

回溯到上个函数,也直接return

技术图片

继续回溯

技术图片

这里调用了return过来的实例的set方法。跟进think\\cache\\driver\\File的set方法

技术图片

可以看到,在142行调用了getCacheKey方法。

技术图片

跟进getCacheKey方法后发现,这里由于options[‘cache_subdir‘]默认值是true,所以这里直接用参数md5加密后的结果的前两位作为目录名,剩余30位作为缓存文件名。然后通过拼接.php后return。

继续回来,获取上面构造的filename之后,在146行将$value进行序列化,然后在149行使用gzcompress对其进行二进制压缩。接着在151行在data前后拼接php标签,最后在152行写文件。

技术图片

这里存在漏洞的点就是151行把用户可控的数据放到了php标签内。

0x03 审计思路

拿到源码后,找到Cache::set(name, value, expire),其中缓存文件名是跟name相关联的,因此可以看作是一个已知条件。漏洞的关键点就是value是否可控。

0x04 补丁

看一下修复之后的结果(v5.0.15)

技术图片

这里在data之前加了一个exit()强制退出,基本杜绝了data执行php代码的可能。

0x05 参考

ThinkPHP 5.0.10-3.2.3 缓存函数设计缺陷可导致 Getshell

以上是关于Thinkphp <= 5.0.10 缓存getshell复现的主要内容,如果未能解决你的问题,请参考以下文章

[thinkphp 5.0源码阅读] 缓存

thinkphp文件缓存怎么找到缓存文件

thinkphp 带检索参数分页

初步学习thinkphp5.0

ThinkPHP 3.2.3 数据缓存与静态缓存

thinkphp5下载文件对原文件有5分钟缓存