前言
漏洞简介
漏洞编号:CVE-2018-10297
漏洞名称:Discuz!跨站脚本攻击
漏洞描述:Discuz! <= X3.4版本存在存储型跨站脚本漏洞,允许远程攻击者注入恶意脚本或HTML代码,从而获取敏感信息或劫持用户会话。
危险等级:中级
提示:以下是本篇文章正文内容,下面案例仅供参考
一、漏洞产生过程
1.漏洞生成点
漏洞在于DZ自带的编辑器,该编辑器在门户发布、修改文章,论坛发布、回复帖子均有使用,导致漏洞影响范围广泛。
portal.php
require './source/class/class_core.php';
$discuz = C::app();
$cachelist = array('portalcategory', 'diytemplatenameportal'); $discuz->cachelist = $cachelist; $discuz->init();
require DISCUZ_ROOT.'./source/function/function_home.php';
require DISCUZ_ROOT.'./source/function/function_portal.php';
if(empty($_GET['mod']) || !in_array($_GET['mod'], array('list', 'view', 'comment', 'portalcp', 'topic', 'attachment', 'rss', 'block'))) $_GET['mod'] = 'index';
define('CURMODULE', $_GET['mod']);
runhooks();
$navtitle = str_replace('{bbname}', $_G['setting']['bbname'], $_G['setting']['seotitle']['portal']); $_G['disabledwidthauto'] = 1;
require_once libfile('portal/'.$_GET['mod'], 'module');
2.跟进portal_portalcp.php
if (!$_G['inajax'] && in_array($ac, array('index', 'portalblock', 'blockdata', 'category', 'plugin')) && ($_G['group']['allowdiy'] || $_G['group']['allowmanagearticle'] || $admincp2 || $admincp3 || $admincp4 || $admincp6)) {
$modsession = new discuz_panel(PORTALCP_PANEL);
if(getgpc('login_panel') && getgpc('cppwd') && submitcheck('submit')) { $modsession->dologin($_G[uid], getgpc('cppwd'), true);
}
if(!$modsession->islogin) {
include template('portal/portalcp_login');
dexit();
}
}
if($ac == 'logout') { $modsession = new discuz_panel(PORTALCP_PANEL);
$modsession->dologout();
showmessage('modcp_logout_succeed', 'index.php');
}
$navtitle = lang('core', 'title_'.$ac.'_management').' - '.lang('core', 'title_portal_management');
require_once libfile('function/portalcp');
require_once libfile('portalcp/'.$ac, 'include');
3.跟进portalcp_article.php
}
$content = getstr($_POST['content'], 0, 0, 0, 0, 1);
$content = censor($content);
if(censormod($content) || $_G['group']['allowpostarticlemod']) {
$article_status = 1;
} else {
$article_status = 0;
}
4.跟进function_home.php
函数getstr实现了对字符串的过滤,但是html的参数设置导致对字符串进行过滤的不是dhtmlspecialchars()函数,而是一个自定义的正则匹配,该正则并未对单、双引号进行过滤,即未对传入的payload进行任何处理。
function getstr($string, $length = 0, $in_slashes=0, $out_slashes=0, $bbcode=0, $html=0) {
global $_G;
$string = trim($string);
$sppos = strpos($string, chr(0).chr(0).chr(0));
if($sppos !== false) {
$string = substr($string, 0, $sppos);
}
if($in_slashes) {
$string = dstripslashes($string);
}
$string = preg_replace("/\[hide=?\d*\](.*?)\[\/hide\]/is", '', $string);
if($html < 0) {
$string = preg_replace("/(\<[^\<]*\>|\r|\n|\s|\[.+?\])/is", ' ', $string);
} elseif ($html == 0) {
$string = dhtmlspecialchars($string);
}
if($length) {
$string = cutstr($string, $length);
}
if($bbcode) {
require_once DISCUZ_ROOT.'./source/class/class_bbcode.php';
$bb = & bbcode::instance();
$string = $bb->bbcode2html($string, $bbcode);
}
if($out_slashes) {
$string = daddslashes($string);
}
return trim($string);
}
5.跟进存储型payload和数据库交互的地方
portalcp_article.php
发布文章则进入插入的代码块,否则进入更新的代码块。跟进执行插入操作的代码块,其中涉及C::t(‘portal_article_content’),可以知道是引入pre_portal_article_content类,对应的操作表为pre_portal_article_content表。
foreach($dbcontents as $key => $value) {
C::t('portal_article_content')->update($value['cid'], array('title' => $pagetitle[$key], 'content' => $contents[$key], 'pageorder' => $key+1));
unset($pagetitle[$key], $contents[$key]);
}
if($cdbcount < $cpostcount) {
foreach($contents as $key => $value) {
C::t('portal_article_content')->insert(array('aid' => $aid, 'id' => $setarr['id'], 'idtype' => $setarr['idtype'], 'title' => $pagetitle[$key], 'content' => $contents[$key], 'pageorder' => $key+1, 'dateline' => TIMESTAMP));
}
$pagecount = $cpostcount;
6.跟进discuz_database
跟进其中调用的insert方法,最终调用的是/upload/source/class/discuz/discuz_database.php文件里discuz_database类中的insert方法
public static function insert($table, $data, $return_insert_id = false, $replace = false, $silent = false) {
$sql = self::implode($data);
$cmd = $replace ? 'REPLACE INTO' : 'INSERT INTO';
$table = self::table($table);
$silent = $silent ? 'SILENT' : '';
return self::query("$cmd $table SET $sql", null, $silent, !$return_insert_id);
}
二、漏洞利用
1.漏洞触发方式
漏洞触发点有两个:
1)在开启论坛功能的情况下,触发反射型XSS; 2)开启门户功能的前提下,通过发布文章实现存储型XSS。
2.漏洞触发方式反射型XSS:
先创建用户xx,以xx的身份发布一篇文章,标题内容随意: 在网络图片地址中输入payload:
z" onerror=alert(1)=
3.提交后审查元素
提交后,F12查看元素,可以发现刚刚输入的payload被页面解析为img标签
4.漏洞利用存储型XSS
存储型XSS利用前提:目标网站开启门户功能,且所在用户组有发表文章的权限。 切换至管理员用户:/dir_SC_UTF8/upload/admin.php
管理员用户名:admin;密码:论坛创建时设置的管理员用户密码。
门户功能:全局—>站点功能—>门户
发布文章:首页—>门户管理—>xx(频道名称)—>发布文章 在网络图片地址中输入payload:http://xxx.xxx.xx.x/1.png"οnclick=alert(document.cookie) = (高度、宽度随机),点击提交,即可将我们生成的文章内容插入数据库
查看内容得到url:/upload/portal.php?mod=view&aid=1
可以看到payload已经被拼接成一个符合正常语法的img标签和a标签,成功存入数据库
5.进行内容审查
查看一下数据库内容:
查看文章中的图片会出现弹窗 弹窗内容为cookie信息
跨站脚本攻击
1.什么是跨站脚本攻击
跨站脚本(Cross-site scripting,简称为 CSS , 但这与层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,因此,跨站脚本攻击缩写为XSS)是一种网站应用程序的安全漏洞攻击
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序,这些恶意网页程序通常是JavaScript,但实际上也可以使Java, VBScript、 LiveScript、ActiveX、 Flash 或者甚至是普通的HTML ,攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作),私密网页内容,会话和cookie等各种内容。
2.XSS类型
常见XSS类型 常见的分类:反射型(非持久型)XSS、存储型(持久型)XSS、DOM型XSS、通用型XSS、突变型XSS。
3.常见攻击手段
XSS平台:可以用来构造一些payload语句,然后在该平台会显示你所要获得的信息
Beef
Cookie:用户凭据,可以用来证明身份信息,cookie和用户登录有关,存储在本地,小中型网站
想要通过xss获取cookie:
①:存在xss跨站漏洞
②:对方浏览器不拦截
③:对方有登录过
④:对方会触发
⑤:对方未过滤
4.常见攻击流程
Ⅰ:找注入点 找到数据输入的地方 判断回显位置—输入的数据在什么地方输出 如果输入的数据能够在前端进行输出,则可以证明输入的前段恶意代码在没有安全性处理的情况下能够输出前端,从而造成风险。 Ⅱ:构造基础的payload 病毒通常会做一些有害的或者恶性的动作。在病毒代码中实现这个功能的部分叫做“有效负载”(payload)。 Ⅲ:进行提交payload 分析响应状况 如果成功解析则XSS存在 反之考虑绕过 Ⅳ:确认漏洞 如果响应达到了预期,则说明漏洞存在,反之不存在。
Discuz!–漏洞修复方式
- 重写编辑器;
- 存储型XSS:将/upload/source/include/portalcp/portalcp_article.php中的
改为
`$content =getstr($_POST['content'], 0, 0, 0, 0, 0);`
修复测试:
修复后的数据库数据:
总结
从开发者角度:
来自应用安全国际组织OWASP的建议,对XSS最佳的防护应该结合以下两种方法:验证所有输入数据,有效检测攻击;对所有输出数据进行适当的编码,以防止任何已成功注入的脚本在浏览器端运行。
从用户角度:
更改浏览器安全设置,建议在浏览器设置中关闭JavaScript。如果使用IE浏览器,将安全级别设置到“高”。
|