## 漏洞概述
蝉知开源版CMS v5.6在`user`模块的`deny()`方法中渲染模板文件时,对用户输入的参数进行渲染,且没有正确处理,导致可绕过一些过滤,从而造成反射性XSS。具体该`deny()`方法在`system/module/user/control.php`文件,该模板文件是`template/default/user/deny.html.php`文件。
## 漏洞分析
`user`模块的`deny()`方法(`/system/module/user/control.php`):
```php
public function deny($module, $method, $refererBeforeDeny = '')
{
$this->app->loadLang($module);
$this->app->loadLang('index');
$this->setReferer();
$this->view->title = $this->lang->user->deny;
$this->view->module = $module;
$this->view->method = $method;
$this->view->denyPage = $this->referer;
$this->view->refererBeforeDeny = $refererBeforeDeny;
$this->view->mobileURL = helper::createLink('user', 'deny', "module=$module&method=$method&referer=$refererBeforeDeny", '', 'mhtml');
$this->view->desktopURL = helper::createLink('user', 'deny', "module=$module&method=$method&referer=$refererBeforeDeny", '', 'html');
die($this->display());
}
```
该方法接收三个参数。假如URI为`www.xxx.com/index.php/user-deny-1-2-3`,这三个参数分别赋值为1,2,3。
#### 第一个反射XSS
来源代码审计小密圈molody分享。访问` http://127.0.0.1/index.php/user-deny-%252522%25253E%25253Cscript%25253Ealert%2525281%252529%25253B%25253C%25252fscript%25253E%25253C%252522` 即可触发。
上面URI携带的payload `%252522%25253E%25253Cscript%25253Ealert%2525281%252529%25253B%25253C%25252fscript%25253E%25253C%252522 `会通过三次URL解码,拼接等,最后渲染在`/template/default/user/deny.html.php`包含的`template/common/header.lite.html.php`文件的`$mobileURL`
这三次URL解码分别是:
1. URI传递到服务器时进行URL解码。
2. 合并默认参数跟用户URI传递的参数时,进行解码。`/system/framework/base/route.class.php`的`mergeParams`方法里:`$defaultParams[$key] = strip_tags(urldecode($passedParams[$i]));`
3. 调用` $this->view->mobileURL = helper::createLink('user', 'deny', "module=$module&method=$method&referer=$refererBeforeDeny", '', 'mhtml');`生成链接时,进行URL解码。`system/framework/helper.class.php`的`createLink()`里:`if(!is_array($vars)) parse_str($vars, $vars);`。 **parse_str()方法在解析字符串时会进行一次URL解码**。生成的链接会被渲染,最后paylaod作为链接的一部分被解码成:`"><script>alert(1);</script><" `。
主要是通过最后一次URL解码,绕过`strip_tags()`过滤。
#### 第二个反射XSS
访问`http://127.0.0.1/index.php/user-deny-1-2-aHR0cDovL3d3dy5iYWlkdS5jb20nPHNjcmlwdD5hbGVydCgzKTs8L3NjcmlwdD4n.html`可触发。
上面的URI传入的payload `aHR0cDovL3d3dy5iYWlkdS5jb20nPHNjcmlwdD5hbGVydCgzKTs8L3NjcmlwdD4n`是经过base64编码的。在渲染的时候会经过一次base64解码。具体在`template/default/user/deny.html.php`文件:
```
if($refererBeforeDeny) echo html::a(helper::safe64Decode($refererBeforeDeny), $lang->user->goback, "class='btn btn-primary'");
```
`$refererBeforeDeny`就是传入的payload。奇怪这里为什么有个base64解码,导致之前的过滤都绕过了。
另外在渲染完前台模板后会调用`/system/framework/control.class.php`文件里的`control`类的`mergeCSS()`和`mergeJS()`来把Javascript代码放在一起,CSS代码放在一起。
以`mergeJS()`为例,这个方法匹配出script标签,具体是
```
preg_match_all('/<script>([\s\S]*?)<\/script>/', $this->output, $scripts);
```
然后把匹配到的script标签的内容合并,插在`</body>`标签上方。
实际上,在没有执行`mergeJS()`之前,我们的payload在页面上是:`<a href='http://www.baidu.com'<script>alert(3);</script>'' class='btn btn-primary'>返回前一页</a>`,这样的js代码是不会解析的,运行了`mergeJS()`之后,`<script>alert(3);</script>`这段代码才给合并到`<script>`标签里,从而成功解析。
另外对于`admin.php`页面(管理员后台页面),不会调用mergeJS()方法,其XSS可为::`http://127.0.0.1/admin.php/?m=user&f=deny&module=1&method=2&refererBeforeDeny=aHR0cDovL3d3dy5iYWlkdS5jb20nPj48c2NyaXB0PmFsZXJ0KDMpOzwvc2NyaXB0Pjwn`
`admin.php`和`index.php`的XSS所达到的效果都是一样的。在这一个反射XSS中,`helper::safe64Decode()`是危险的方法,导致绕过了过滤。而`mergeJS()`方法提供了一点不紧要的帮助。
## 漏洞证明
访问 `http://127.0.0.1/index.php/user-deny-%252522%25253E%25253Cscript%25253Ealert%2525281%252529%25253B%25253C%25252fscript%25253E%25253C%252522` 即可触发。

访问`http://127.0.0.1/index.php/user-deny-1-2-aHR0cDovL3d3dy5iYWlkdS5jb20nPHNjcmlwdD5hbGVydCgzKTs8L3NjcmlwdD4n.html`可触发。

暂无评论