本文来源自平安银河实验室
作者:李思锐
漏洞分析
/**
* Check the user has permission to access this method, if not, locate to the login page or deny page.
*
* @access public
* @return void
*/
public function checkPriv()
{
try
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if($this->app->isFlow)
{
$module = $this->app->rawModule;
$method = $this->app->rawMethod;
}
$beforeValidMethods = array(
'user' => array('deny', 'logout'),
'my' => array('changepassword'),
'message' => array('ajaxgetmessage'),
);
if(!empty($this->app->user->modifyPassword) and (!isset($beforeValidMethods[$module]) or !in_array($method, $beforeValidMethods[$module]))) return print(js::locate(helper::createLink('my', 'changepassword')));
if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();
if($this->isOpenMethod($module, $method)) return true;
if(isset($this->app->user))
{
$this->app->user = $this->session->user;
if(!commonModel::hasPriv($module, $method))
{
if($module == 'story' and !empty($this->app->params['storyType']) and strpos(",story,requirement,", ",{$this->app->params['storyType']},") !== false) $module = $this->app->params['storyType'];
$this->deny($module, $method);
}
}
else
{
$uri = $this->app->getURI(true);
if($module == 'message' and $method == 'ajaxgetmessage')
{
$uri = helper::createLink('my');
}
elseif(helper::isAjaxRequest())
{
die(json_encode(array('result' => false, 'message' => $this->lang->error->loginTimeout))); // Fix bug #14478.
}
$referer = helper::safe64Encode($uri);
die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
}
}
catch(EndResponseException $endResponseException)
{
echo $endResponseException->getContent();
}
}
{
try
{
// code
}
catch(EndResponseException $endResponseException)
{
echo $endResponseException->getContent();
}
}
public function checkPriv()
{
try
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if($this->app->isFlow)
{
$module = $this->app->rawModule;
$method = $this->app->rawMethod;
}
$beforeValidMethods = array(
'user' => array('deny', 'logout'),
'my' => array('changepassword'),
'message' => array('ajaxgetmessage'),
);
if(!empty($this->app->user->modifyPassword) and (!isset($beforeValidMethods[$module]) or !in_array($method, $beforeValidMethods[$module]))) return print(js::locate(helper::createLink('my', 'changepassword')));
if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();
if($this->isOpenMethod($module, $method)) return true;
if(isset($this->app->user))
{
$this->app->user = $this->session->user;
if(!commonModel::hasPriv($module, $method))
{
// 需要进入这个逻辑才能触发
if($module == 'story' and !empty($this->app->params['storyType']) and strpos(",story,requirement,", ",{$this->app->params['storyType']},") !== false) $module = $this->app->params['storyType'];
$this->deny($module, $method);
}
}
else
{
$uri = $this->app->getURI(true);
if($module == 'message' and $method == 'ajaxgetmessage')
{
$uri = helper::createLink('my');
}
elseif(helper::isAjaxRequest())
{
die(json_encode(array('result' => false, 'message' => $this->lang->error->loginTimeout))); // Fix bug #14478.
}
$referer = helper::safe64Encode($uri);
die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
}
}
catch(EndResponseException $endResponseException)
{
echo $endResponseException->getContent();
}
}
-
$this->app->user 即 user 存在
-
!commonModel::hasPriv($module, $method) 没有权限访问指定模块的指定方法
-
session 中有 user 对象,但是对 user 具体内容没有要求,比如guest
-
访客模式
-
命令行模式下调用
public function isOpenMethod($module, $method)
{
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;
if($module == 'block') return true;
if($module == 'my' and $method == 'guidechangetheme') return true;
if($module == 'misc' and $method == 'downloadclient') return true;
if($module == 'misc' and $method == 'changelog') return true;
if($module == 'tutorial' and $method == 'start') return true;
if($module == 'tutorial' and $method == 'index') return true;
if($module == 'tutorial' and $method == 'quit') return true;
if($module == 'tutorial' and $method == 'wizard') return true;
if($module == 'product' and $method == 'showerrornone') return true;
}
return false;
}
-
“$module.$method” 在 config/zentaopms.php定义里面
-
$module == ‘block’ and $method == ‘main’ and isset($_GET[‘hash’])
-
$this->loadModel(‘user’)->isLogon() or ($this->app->company->guest and $this->app->user->account == ‘guest’) 即 登录状态或者访客
/**
* Show captcha and save to session.
*
* @param string $sessionVar
* @param string $uuid
* @access public
* @return void
*/
public function captcha($sessionVar = 'captcha', $uuid = '')
{
$obLevel = ob_get_level();
for($i = 0; $i < $obLevel; $i++) ob_end_clean();
header('Content-Type: image/jpeg');
$captcha = $this->app->loadClass('captcha');
$this->session->set($sessionVar, $captcha->getPhrase());
$captcha->build()->output();
}
命令注入
因为是命令注入,优先查找 exec 关键字/ 组件功能模块,尝试发现diff内容中是否有相关内容,发现 lib/scm/gitrepo.class.php含有相关内容,这里对$client加了一次校验,容易看出这里存在利用的风险,后面补丁做了一次加固。
验证过程
验证成功图片
银河实验室
好 文 回 顾
点赞、分享,感谢你的阅读▼
原文始发于微信公众号(平安集团安全应急响应中心):项目管理系统远程命令执行漏洞