首页 > 其他分享 >禅道系统权限绕过与命令执行漏洞分析

禅道系统权限绕过与命令执行漏洞分析

时间:2023-03-09 19:32:59浏览次数:55  
标签:0px span 1000% max padding 漏洞 权限 width 禅道

漏洞概述


禅道是第一款国产的开源项目管理软件,也是国内最流行的项目管理软件。该系统在2023年初被爆出在野命令执行漏洞,官方已于2023年1月12日发布了漏洞修复补丁。该漏洞是由于禅道项目管理系统权限认证存在缺陷导致,可利用该漏洞在未授权的情况下,通过权限绕过在服务器执行任意命令。


本文以安全研究为目的,分享对该漏洞的研究和复现过程,仅供学习和参考。由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用者本人负责,文章作者不为此承担任何责任。


影响范围



禅道系统



影响版本



开源版



17.4以下的未知版本<=version<=18.0.beta1



旗舰版



3.4以下的未知版本<=version<=4.0.beta1



企业版



7.4以下的未知版本<=version<=8.0.beta1 8.0.beta2



复现环境


操作系统:macOS 13.1

运行环境:nginx1.5 php7.4 mysql5.7

软件版本:zentaopms-zentaopms_18.0.beta1


权限绕过-漏洞分析


权限绕过的关键点在module/common/model.php文件中checkPriv函数,此函数是检查权限的函数,验证当前登陆用户是否有访问module与method的权限。分析代码后得知在没有访问权限时会抛出异常,但是代码中并没有终止程序,只是输出权限不足的内容。具体代码如下:



1


​<code><span style=​​​​"padding:0px; max-width:1000%;"​​​​> <span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>public<​​​​/span​​​​> ​​​​function​​​ ​​<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>checkPriv<​​​​/span​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>()<​​​​/span​​​​><​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>{<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>try<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​> = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->getModuleName();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            $method = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->getMethodName();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->isFlow)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​> = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->rawModule;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                $method = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->rawMethod;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            }<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            $beforeValidMethods = <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>array<​​​​/span​​​​>(<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'user'​​​​<​​​​/span​​​​>    => <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>array<​​​​/span​​​​>(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'deny'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'logout'​​​​<​​​​/span​​​​>),<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'my'​​​​<​​​​/span​​​​>      => <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>array<​​​​/span​​​​>(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'changepassword'​​​​<​​​​/span​​​​>),<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'message'​​​​<​​​​/span​​​​> => <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>array<​​​​/span​​​​>(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'ajaxgetmessage'​​​​<​​​​/span​​​​>),<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            );<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(!empty($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->user->modifyPassword) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> (!isset($beforeValidMethods[$<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​>]) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>or<​​​​/span​​​​> !in_array($method, $beforeValidMethods[$<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​>]))) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​return​​​​<​​​​/span​​​​> print(js::​​​​locate​​​​(helper::createLink(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'my'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'changepassword'​​​​<​​​​/span​​​​>)));<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->isOpenMethod($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​>, $method)) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​return​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​true​​​​<​​​​/span​​​​>;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(!$<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->loadModel(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'user'​​​​<​​​​/span​​​​>)->isLogon() <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->server->php_auth_user) $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->user->identifyByPhpAuth();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(!$<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->loadModel(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'user'​​​​<​​​​/span​​​​>)->isLogon() <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->cookie->za) $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->user->identifyByCookie();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(isset($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->user))<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(in_array($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​>, $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->config->programPriv->waterfall) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->tab == <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'project'​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> $method != <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'browse'​​​​<​​​​/span​​​​>) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​return​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​true​​​​<​​​​/span​​​​>;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->user = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->session->user;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(!commonModel::hasPriv($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​>, $method))<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                    <br>                    <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​> == <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'story'​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> !empty($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->params[<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'storyType'​​​​<​​​​/span​​​​>]) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> strpos(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​",story,requirement,"​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​",{$this->app->params['storyType']},"​​​​<​​​​/span​​​​>) !== <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​false​​​​<​​​​/span​​​​>) $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​> = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->params[<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'storyType'​​​​<​​​​/span​​​​>];<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                    <br>                    $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->deny($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​>, $method);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                }<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            }<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​else​​​​<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                $uri = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->getURI(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​true​​​​<​​​​/span​​​​>);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>module<​​​​/span​​​​> == <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'message'​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>and<​​​​/span​​​​> $method == <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'ajaxgetmessage'​​​​<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                    <br>                    $uri = helper::createLink(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'my'​​​​<​​​​/span​​​​>);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                }<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                elseif(helper::isAjaxRequest())<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                <br>                {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>                    <br>                    die(json_encode(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>array<​​​​/span​​​​>(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'result'​​​​<​​​​/span​​​​> => <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​false​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'message'​​​​<​​​​/span​​​​> => $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->lang->error->loginTimeout))); <span style=​​​​"padding:0px; max-width:1000%; color:rgb(175, 175, 175); font-style:italic;"​​​​>​​​​//​​​ ​​Fix bug ​​​​#14478.</span></span></code><code><span style="padding:0px; max-width:1000%;">                }</span></code><code><span style="padding:0px; max-width:1000%;"><br></span></code><code><span style="padding:0px; max-width:1000%;">                $referer = helper::safe64Encode($uri);</span></code><code><span style="padding:0px; max-width:1000%;">                <br>                die(js::locate(helper::createLink(<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'user'</span>, <span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'login'</span>, <span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">"referer=$referer"</span>)));</span></code><code><span style="padding:0px; max-width:1000%;">            <br>            }</span></code><code><span style="padding:0px; max-width:1000%;">        <br>        }</span></code><code><span style="padding:0px; max-width:1000%;">        <br>        <span style="padding:0px; max-width:1000%; color:rgb(202, 125, 55);">catch</span>(EndResponseException $endResponseException)</span></code><code><span style="padding:0px; max-width:1000%;">        <br>        {</span></code><code><span style="padding:0px; max-width:1000%;">            <br>            echo $endResponseException->getContent();</span></code><code><span style="padding:0px; max-width:1000%;">        <br>        }</span></code><code><span style="padding:0px; max-width:1000%;">    <br>    }</span></code>​



其中commonModel::hasPriv()函数是内置公共的验证权限,代码中可以看出无权限访问就会执行deny 方法,而deny 最后验证的结果是无权限则执行helper::end(),该方法是直接抛出异常,就会进入上面的try  cache逻辑。


1

2

3

4


​public static ​​​​function​​​ ​​end($content = ​​​​''​​​​)    ​

​{        ​

​throw EndResponseException::create($content);    ​

​}​



在进入权限检查的流程前需要在$this->app->user 不为空的情况下将 $this->session->user赋值给 $this->app->user ,然后再做权限检查。因此我们还需要构造一个$this->session->user,即写一个session['user']才能进行绕过。所以现在思路很清晰了,只需$this->session->user 存在就可以通过⽤户是否登录的检查,使权限检查的函数如同虚设。 根据这个思路逆推可以得出结论:只要有任意⼀个⽤户session就可以调⽤任意模块的任意⽅法。


经过代码审计发现captcha函数可以直接写入一个自定义key的session,此段代码本意是设置生成一个自定义session的key的验证码,开发者应该是想写一个公共的验证码生成函数让其他开发者做新功能需要的时候可以直接调用,正好可以利用生成一个key为user的session。


1


​<code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>public<​​​​/span​​​​> ​​​​function​​​ ​​<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>captcha<​​​​/span​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>($sessionVar = <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'captcha'​​​​<​​​​/span​​​​>, $uuid = <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $obLevel = ob_get_level();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ​​​​for​​​​($i = 0; $i < $obLevel; $i++) ob_end_clean();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        header(​​​​'</span>Content-Type: image/jpeg<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​​);<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $captcha = $this->app->loadClass(​​​​'</span>captcha<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​​);<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $this->session->​​​​set​​​​($sessionVar, $captcha->getPhrase());<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $captcha->build()->output();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    }<​​​​/span​​​​><​​​​/code​​​​>​



通过上述思路可以成功实现权限绕过,不过经过实际测试发现,能绕过访问的皆为公共模块。因为在禅道的功能权限验证中还有一部分是验证userid或level。就好比某些用户有“项目1”的权限,某些用户有“项目2”的权限,所以类似这类的数据任然不能访问获取。


命令执行-漏洞分析

实际上整个利用链最关键的一环就在上面的权限绕过上,禅道系统后台本身存在多个sql注入及命令执行漏洞,本文给出一种后台命令执行的方法供参考,其他利用点感兴趣的小伙伴可自行研究。

在权限绕过后,接下来我们需要分析后台命令执行点的位置。通过代码审计,最终锁定在module/repo/model.php文件,其中checkConnection函数会进行SCM=Subversion判断,$client是导致命令注入的参数点,一条完整的函数间调用的利用过程如下所示:


module/repo/model.php->create()

module/repo/control.php->edit ()

module/repo/model.php->update($repoID)->checkConnection()->exec($versionCommand,$versionOutput, $versionResult);


PS:为什么要创建仓库,因为在查看checkConnection调用函数为create和update,但是在create的时候必须经过checkClient 的判断,必须要创建一个文件才行,如果SCM指定为Gitlab就不需要通过checkClient判断。


禅道系统权限绕过与命令执行漏洞分析_php


具体复现思路如下:


1.进入创建仓库的函数:module/repo/model.php


1


​<code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>public<​​​​/span​​​​> ​​​​function​​​ ​​<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>create<​​​​/span​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>()<​​​​/span​​​​><​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>{<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(!$<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->checkClient()) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​return​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​false​​​​<​​​​/span​​​​>;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(!$<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->checkConnection()) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​return​​​​<​​​​/span​​​​> <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​false​​​​<​​​​/span​​​​>;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    $isPipelineServer = in_array(strtolower($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->post->SCM), $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->config->repo->gitServiceList) ? <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​true​​​​<​​​​/span​​​​> : <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>​​​​false​​​​<​​​​/span​​​​>;<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    $data = fixer::input(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'post'​​​​<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($isPipelineServer, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'password'​​​​<​​​​/span​​​​>, $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->post->serviceToken)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->post->SCM == <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'Gitlab'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'path'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($this->post->SCM == ​​​​'</span>Gitlab<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'client'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($this->post->SCM == ​​​​'</span>Gitlab<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'extra'​​​​<​​​​/span​​​​>, $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->post->serviceProject)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($isPipelineServer, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'prefix'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($this->post->SCM == ​​​​'</span>Git<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'account'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setIf($this->post->SCM == ​​​​'</span>Git<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'password'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->skipSpecial(​​​​'</span>path,client,account,password<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setDefault(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'product'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->​​​​join​​​​(​​​​'</span>product<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​','​​​​<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->setDefault(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'projects'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​)->​​​​join​​​​(​​​​'</span>projects<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​','​​​​<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ->get();<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    $data->acl = empty($data->acl) ? <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​ ​​: json_encode($data->acl);<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    ​​​​if​​​​($data->SCM == ​​​​'</span>Subversion<span style="padding:0px; max-width:1000%; color:rgb(14, 156, 229);">'​​​​<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $scm = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->app->loadClass(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'scm'​​​​<​​​​/span​​​​>);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $scm->setEngine($data);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $info     <br>        = $scm->info(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​''​​​​);<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $infoRoot = urldecode($info->root);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $data->prefix = empty($infoRoot) ? ​​​​'</span><span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​ ​​: trim(str_ireplace($infoRoot, ​​​​'</span><span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​​, str_replace(​​​​'</span>\\<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​​, ​​​​'</span>/<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​​, $data->path)), ​​​​'</span>/<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​​);<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        ​​​​if​​​​($data->prefix) $data->prefix = ​​​​'</span>/<span style="padding:0px; max-width:1000%; color:rgb(221, 17, 68);">'​​​ ​​. $data->prefix;<​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>    <br>    }<​​​​/span​​​​><​​​​/code​​​​>​



当SCM类型指定为Subversion时,后续控制$client才可以完成命令注入。


2.编辑代码仓库进入module/repo/control.php中的edit函数,post传参会进入到update函数。


1


​<code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​><span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>public<​​​​/span​​​​> ​​​​function​​​ ​​<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>edit<​​​​/span​​​​><span style=​​​​"padding:0px; max-width:1000%;"​​​​>($repoID, $objectID = <span style=​​​​"padding:0px; max-width:1000%; color:rgb(14, 156, 229);"​​​​>0<​​​​/span​​​​>)<​​​​/span​​​​><​​​​/span​​​​><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>{<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->commonAction($repoID, $objectID);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​><br><​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        $repo = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->repo->getRepoByID($repoID);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>($_POST)<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>        <br>        {<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            $noNeedSync = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->repo->update($repoID);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​if​​​​<​​​​/span​​​​>(dao::isError()) <span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>​​​​return​​​​<​​​​/span​​​​> $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->send(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>array<​​​​/span​​​​>(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'result'​​​​<​​​​/span​​​​> => <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'fail'​​​​<​​​​/span​​​​>, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'message'​​​​<​​​​/span​​​​> => dao::getError()));<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>           <br>           $newRepo  = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->repo->getRepoByID($repoID);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            $actionID = $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->loadModel(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'action'​​​​<​​​​/span​​​​>)->create(<span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'repo'​​​​<​​​​/span​​​​>, $repoID, <span style=​​​​"padding:0px; max-width:1000%; color:rgb(221, 17, 68);"​​​​>​​​​'edited'​​​​<​​​​/span​​​​>);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            $changes  = common::createChanges($repo, $newRepo);<​​​​/span​​​​><​​​​/code​​​​><code><span style=​​​​"padding:0px; max-width:1000%;"​​​​>            <br>            $<span style=​​​​"padding:0px; max-width:1000%; color:rgb(202, 125, 55);"​​​​>this<​​​​/span​​​​>->action->logHistory($actionID, $changes);<​​​​/span​​​​><​​​​/code​​​​>​



跟踪update函数到module/repo/model.php,需要将scm设置为Subversion,此时会去检测svn服务器是否可以连接。


1

2

3

4

5

6

7

8

9

10

11

12

13

14


​public ​​​​function​​​ ​​update($​​​​id​​​​){        ​

​$repo = $this->getRepoByID($​​​​id​​​​);        ​

​if​​​​(!$this->checkConnection()) ​​​​return​​​ ​​false​​​​;        ​

​$isPipelineServer = in_array(strtolower($this->post->SCM),$this->config->repo->gitServiceList) ? ​​​​true​​​ ​​: ​​​​false​​​​;        ​

​$data = fixer::input(​​​​'post'​​​​)            ​

​->setIf($isPipelineServer, ​​​​'password'​​​​, $this->post->serviceToken)            ​

​->setIf($this->post->SCM == ​​​​'Gitlab'​​​​, ​​​​'path'​​​​, ​​​​''​​​​)            ​

​->setIf($this->post->SCM == ​​​​'Gitlab'​​​​, ​​​​'client'​​​​, ​​​​''​​​​)            ​

​->setIf($this->post->SCM == ​​​​'Gitlab'​​​​, ​​​​'extra'​​​​, $this->post->serviceProject)            ​

​->setDefault(​​​​'prefix'​​​​, $repo->prefix)            ​

​->setIf($this->post->SCM == ​​​​'Gitlab'​​​​, ​​​​'prefix'​​​​, ​​​​''​​​​)            ​

​->setDefault(​​​​'client'​​​​, ​​​​'svn'​​​​)            ​

​->setDefault(​​​​'product'​​​​, ​​​​''​​​​)            ​

​->skipSpecial(​​​​'path,client,account,password'​​​​)​



跟踪该函数,$this->post->SCM等于Subversions时会去check svn服务器version,此刻会把$this->post->client拼接到执行的versionCommand 中,造成命令执行。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23


​if​​​​(empty($_POST)) ​​​​return​​​ ​​false​​​​;        ​

​$scm     ​

​= $this->post->SCM;        ​

​$client   ​

​= $this->post->client;        ​

​$account  = $this->post->account;        ​

​$password = $this->post->password;        ​

​$encoding = strtoupper($this->post->encoding);        ​

​$path     ​

​= $this->post->path;        ​

​if​​​​($encoding != ​​​​'UTF8'​​​ ​​and $encoding != ​​​​'UTF-8'​​​​) $path = helper::convertEncoding($path, ​​​​'utf-8'​​​​, $encoding);        ​

​if​​​​($scm == ​​​​'Subversion'​​​​)        ​

​{            ​

​/* Get svn version. */            ​

​$versionCommand = ​​​​"$client --version --quiet 2>&1"​​​​;            ​

​exec​​​​($versionCommand, $versionOutput, $versionResult);            ​

​if​​​​($versionResult)            ​

​{                ​

​$message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, ​​​​join​​​​(​​​​"<br />"​​​​, $versionOutput));                ​

​dao::$errors[​​​​'client'​​​​] = $this->lang->repo->error->cmd . ​​​​"<br />"​​​ ​​. nl2br($message);                ​

​return​​​ ​​false​​​​;            ​

​}            ​

​$svnVersion = end($versionOutput);​



命令执行最终效果截图:


禅道系统权限绕过与命令执行漏洞分析_php_02

修复建议

目前禅道官方已正式发布修复版本,建议受影响用户尽快升级至安全版本。


如不能升级,可在module/common/model.php文件中的echo $endResponseException->getContent();后面加上exit(); 来修复权限绕过漏洞。


烽火台实验室公众号回复“zentaopoc”可获取漏洞自检脚本,该脚本仅用于检测自有系统是否存在漏洞,若确认漏洞存在请尽快进行版本升级和修复。


原文链接

标签:0px,span,1000%,max,padding,漏洞,权限,width,禅道
From: https://blog.51cto.com/u_15634773/6111125

相关文章

  • 玩转CodeQLpy之用友GRP-U8漏洞挖掘
    0x01前言CodeQLpy是作者使用python3实现的基于CodeQL的java代码审计工具,github地址https://github.com/webraybtl/CodeQLpy。通过CodeQLpy可以辅助代码审计人员快速定位代......
  • .grf 读入报表模板失败,请检查网络连接、URL及读取权限,错误提示:网络服务器响应不成功
    http://xxx.xxx.xxx.xxx:xxx/Pages/QySqdInfo/../../GrfFile/Qianyue/qianyue_3303.grf从'http://xxx.xxx.xxx.xxx:xxx/Pages/QySqdInfo/../../GrfFile/Qianyue/qianyue_330......
  • MySQL开放远程连接权限
    创建一个新的MySQL用户,命令行登录mysql,通过语句创建一个新用户CREATEUSER'username'@'%'IDENTIFIEDBY'your_user_password';让我们授予这个用户一些数据库权限,例......
  • Linux给用户赋予对某个文件夹的操作权限
    转载自:https://cloud.tencent.com/developer/article/2100610============ 1、切换到root用户su-root 2、更改文件夹的用户为user1chown-Ruser1:user1/......
  • CVE-2016-3348漏洞分析
    0x00漏洞信息漏洞影响:本地提权漏洞文件:win32kfull.sys漏洞函数:GreDoBanding漏洞原因:释放重引用漏洞日期: 2016年9月13日【漏洞分析合集】0x01漏洞分析win32kf......
  • 开源API越权漏洞检测系统推荐:IDOR_detect_tool
    相信大部分读者跟我一样,每天都在写各种API为Web应用提供数据支持,那么您是否有想过您的API是否足够安全呢?Web应用的安全是网络安全中不可忽视的关键方面。我们必须确保其We......
  • 禅道使用流程
    新手教程开源体验版-12admin123456开源体验版-18admin1234561.创建产品2.添加需求产品-需求-提需求,把需求放上去。3.创建项目然后选择团队,设置各......
  • GitHub 漏洞:黑客窃取了 GitHub Desktop 和 Atom 的代码签名证书
    GitHub披露,未知的威胁行为者设法泄露了与适用于Mac和Atom应用程序的某些版本的GitHubDesktop有关的加密代码签名证书。因此,该公司出于谨慎考虑采取了吊销暴露......
  • TypechoCMS 反序列化漏洞
    前言这学期的专业实习周的第七个实验:TypechoCMS反序列化漏洞的代码审计,因为是学校的实验内容,所以难度也会比较低一些环境搭建TypechoCMS官方:https://typecho.org/漏......
  • Pwn2Own Austin 2021 Cisco RV34x RCE 漏洞链复现
    前言这个RCE漏洞利用链的实现是由几个逻辑洞的结合而导致的,这几天我花了一些时间复现了一遍,在此记录一下。固件解压我下载的是RV345v1.0.03.24,从官网下载到压缩包解压......