一次SWF XSS挖掘和利用

author:p.z

[ 目录 ]

[0x00] 背景

[0x01] 挖掘漏洞

[0x02] 优雅利用

[0x03] 从反射到rootkit

[0x04] 总结

[0x00] 背景

这篇迟到了近一年的paper是对WooYun: Gmail某处XSS可导致账号持久劫持 漏洞的详细说明,赶在世界末日发布,希望不会太晚.:)

既然标题已经提到了SWF XSS,那么第一件事就是查找mail.google.com域下的所有swf文件.感谢万能的Google,利用下面的dork,可以搜索任意域的swf, "site:yourdomain.com filetype:swf",进行简单的去重之后,我们得到了如下几个swf文件:

通过文件名以及直接打开,对这些的swf的功能应该有了一个初步的判断. chatsound.swf和sound.swf应该是播放声音用的, uploaderapi2.swf是上传文件, audio.swf是播放音频文件, media-api.swf? 还是不知道干嘛用的... 然后直接在Google里搜索这些swf的地址, 可以得到一些含有swf的地址, 比如"https://mail.google.com/mail/html/audio.swf?audioUrl= Example MP3 file", 通过这些swf后面跟的参数, 我们可以进一步推测出这个swf的功能, 此外在反编译时搜索这些参数, 可以快速地定位到整个swf的初始化的过程. 通过以上的过程, 我们发现, 该swf不仅仅接受audioUrl参数, 还接受videoUrl参数, 说明它还是一个视频播放器, 功能上的复杂化必然会对应用的安全性有所影响, 我们决定对此SWF文件进行深入分析.

[0x01] 挖掘漏洞

下载反编译后得到该swf所有的as文件, 通过搜索'ExternalInterface.call', 'getURL', 'navigateToURL', 'javascript:'等关键函数和字符串, 可以快速地定位一些能够执行javascript的代码段. 当搜索'javascript:'时, 我们得到了如下有意思的代码:

一个类似javascript伪协议的字符串被代入了queueURL函数, 而且最为关键的是this.mediaPlayer_.url是被直接拼接到字符串的, 并未加以对引号的转义. 但说这是一个xss漏洞还为时过早, 因为我们不知道queueURL函数到底是做什么的, 而且对于this.mediaPlayer_.url在赋值之前是否有进行过滤, 还是处于一个未知的状态. 此外通过对函数名的判断,onPlaybackComplete应该是一个在播放完毕之后的回调函数.

我们搜索到了函数queueURL被定义的地方, 代码如下:

然后通过跟踪"urlQueue_"变量, 发现如下代码:

继续跟踪"checkForPageChanges"函数:

搜索"initPlayerWithVars"函数:

从函数名字initializePlayer推断, 这个应该是一个初始化播放器的函数, 在swf打开的时候应该会被执行. 通过搜索的结果, 对整个过程进行反演:initializePlayer函数初始化播放器, 通过对(this.mediaState_ != undefined && (this.mediaState_.videoUrl != undefined || this.mediaState_.audioUrl != undefined))这一逻辑的判读, 如果为true, 则执行initPlayerWithVars函数, 每隔100毫秒调用checkForPageChanges函数, checkForPageChanges函数会检查urlQueue_是否为空数组, 如果不为空, 则弹出数组成员, 直接传入getURL函数. 而onPlaybackComplete则是一回调函数, 当播放完成后自动调用, 如果满足逻辑(this.playerMode_ == com.google.ui.media.MediaPlayer.PLAYER_MODE_NORMAL || this.playerMode_ == com.google.ui.media.MediaPlayer.PLAYER_MODE_MINI), 会把this.mediaPlayer_.url参数压入urlQueue_数组.

通过以上跟踪分析, 我想我们可以得到第一个疑问的答案了,this.mediaPlayer_.url参数最终会被传入到getURL函数. 现在要来看mediaPlayer_.url参数是怎么取到的.

搜索mediaPlayer_.url:

通过上述代码可以发现mediaPlayer_.url可以从两个地方获取, mediaState_.videoUrl和mediaState_.audioUrl. 现在再回过头来看文章开头的地方提到两个参数, videoUrl和audioUrl, 我们推断mediaState_.videoUrl和mediaState_.audioUrl参数是从url中传入的. 为了验证这一的想法, 我把audio.swf放置在本地服务器上, 并自己写了一个swf去读取audio.swf中的mediaState_.videoUrl和mediaState_.audioUrl. 当我载入http://localhost/gmail/audio.swf?videoUrl=http://localhost/test.flv时, 发现读取到的mediaState_.videoUrl为空.看来事情并没有我们想象的那么简单.

我们继续来跟代码. mediaState_应该是一个类的实例, 通过实例的名字, 我们猜测类名可能是mediaState, 搜索mediaState, 果然存在这个类:com.google.video.apps.MediaState. 阅读代码, 我们发现了读取mediaState_.videoUrl值失败的关键逻辑

看来swf对从url传入的值进行了检查. 我们接着跟踪com.google.webutil.url.Utils.isValidVideoUrl和com.google.webutil.url.Utils.isValidAbsoluteGoogleUrl这两个函数.

现在回想一下我们利用成功的前提条件, 就是需要函数没有在对mediaState_.videoUrl或mediaState_.audioUrl赋值时进行引号的转义. 阅读以上的代码, 我们发现验证函数并没有任何对引号进行转义操作, 说明这个漏洞的确是存在的.:) 但是别高兴地太早了, 在回过头想一下触发getURL的函数onPlaybackComplete, 没错, 是一个回调函数, 需要视频流或者音频流播放完毕, 因此, 我们必须要寻找一个确实存在的视频或者音频文件, 且能满足以上对于url的检查. 由于audio.swf文件创建时间比较早, isValidVideoUrl函数中检验的几个api均已经废弃了, 因此我们转向检查较为宽松的isValidAbsoluteGoogleUrl的函数以寻求突破.

我们来看下com.google.webutil.url.Utils.getProtocolAndHost这个关键函数.

注意getProtocolAndHost函数中varloc5 = _loc3.lastIndexOf(":")这行代码, 我想程序员的本意是想利用这个":"获取web应用的端口, 如localhost:8080之类的, 但是在uri中,还有一个地方是需要":"的, 就是在401登陆中, 作为用户名和密码的分割符, 而且这个":"出现的位置是在作为分割host和端口的":"之前. 利用这个特性,我们就可以很轻松地绕过isValidAbsoluteGoogleUrl的检查了. 载入http://localhost/gmail/audio.swf?audioUrl=http://google.com:@localhost/t.mp3时, 成功地读取到的mediaState.audioUrl的值,就是http://google.com:@localhost/t.mp3.

再加上其他参数,使得能满足上述的一些if判断,最后的poc如下:

URL解码后如下

我们拼接最后传入getURL的伪协议字符串

由于在承载swf的html页面中FlashRequest未定义, 我们需要自己定义一个FlashRequest函数, 而且在js中, function语句是最先执行的, 所以不用担心在执行FlashRequest('donePlaying', 'http://google.com:@localhost/gmail/t.mp3?')这句时FlashRequest还没有定义. 当然, 你可以把alert(document.domain)转换成任意你想要执行的js代码. 另外值得注意的一点就是, 由于getURL操作在mp3播放完毕后才触发的, 因此我们把http://localhost/t.mp3剪切得足够短, 只有0.5秒, 当你打开swf之后, 不到一秒钟, MP3已经载入并播放完毕, js得到了执行, 你很难察觉到其中的延迟.

[0x02] 优雅利用

对于一个完美主义者, 我们不得不承认, 上述提到的poc是丑陋的. 原因如下:

那么如何形成一个更加优雅的利用方式呢?

我在查找fromArgs函数时, 发现以下的代码

我想大概有两个办法可以载入一段视频, 一个是直接赋值一个videoUrl, 正如前文提到的, 另一个就是通过制定一个mediarss, swf去解析这个rss, 播放其中指定的视频, 更美妙的是, 对于mediarss, 只判断是是否是绝对地址(isValidAbsoluteUrl), 这使得载入我们直接服务器上的mediarss文件成为了可能.

让我们忘记所有的代码吧, 对于这种xml文件类的调试, 我想以黑盒的方式更加方便一些. 再感谢万能的Google, 我从网上找到了一份mediarss的样本, 修改如下, 我们替换了中的, 改成自己服务器上的flv文件地址. 上传到http://localhost/gmail/media.xml.

现在打开https://mail.google.com/mail/html/audio.swf?playerMode=normal&mediarss=http://localhost/gmail/media.xml&autoplay=true 我们惊喜地发现1.flv被成功地载入了, 看来swf对于从mediarss中传入的videoUrl, 并没有类似对url中传入videoUrl值的一个有效性验证, 短板理论在这里发挥了作用:)

为了更加完美地利用, 我们继续对media.xml进行一些小小的修改.

其中1.flv是一段黑色背景, 无任何内容, 长0.5秒的视频, 播放1.flv结束后, 触发了事件, 通过getURL执行了js脚本, 与此同时,swf开始播放2.flv, 这是一段真正的有内容的视频, 他可以为我们争取到足够的时间进行一些dirty work了. 那么对于点开视频的人来说, 这段时间内究竟发生了什么呢, 他只是照常点了一个连接,发现是一段有意思的小视频, 可能这段视频前面有0.5秒的停滞,但又有谁会注意到呢?

[0x03] 从反射到rootkit

现在我们有一个利用十分优雅的xss了, 哦, 对不起, 但是这只是一个反射型xss, 用处似乎不大? 我们能不能把这个反射型xss转化成一个存储型的呢, 甚至更进一步, 变成一个rootkit, 一个xss shell, 使得用户在每一次打开gmail时都可中枪, 执行我们的js.

-什么?不可能?

-风太大,听不见. - .-

下面就看这个神奇的show吧. Gmail里面有个lab, 提供一些新奇的实验性的小玩意, 其中有一个就是"Add any gadget by URL". 你可以指定一个特定的gadget网址, 这个gadget会出现在你gmail页面的左下角. 用户可以往gadget里添加任意的html代码, 包括script, Google通过把这个gadget的域名设定为googleusercontent.com, 然后以iframe的方式载入, 来避免可能产生的安全问题. 但这个是否足够安全呢, 在大多数情况下, 是的. 可我们现在有了一个反射型xss, 这个答案就不一样了.

我根据digg.com的gadget做了一些修改, iframe了一个我们刚刚挖掘的gmail反射型xss.

整个攻击流程如下:

整个过程视频如下http://v.youku.com/v_show/id_XMzU3MjQ1NTQw.html.

[0x04] 总结

挖掘swf的漏洞并没有很难, 因为引起xss的函数最终也就这么几个, 你可以先搜索这几个关键函数, 再逆向跟踪引用它的函数, 譬如本文, 或者也可以先跟踪所有被传入swf的变量, 在跟踪这些变量的时候查看是否存在有漏洞的输出. 相比于审核JS文件中的dom输出, swf里的审核里显得方便了许多, 而且google的swf并没有类似google的js的混淆压缩, 代码更加友好. 但从调试工具上来说, swf就比JS薄弱了许多, 因此对于审核swf文件来说, 足够的耐心就显得比较重要了, 本文在第一步挖掘中跟踪了五级函数, 但实际上并没有描述那么简单, 因为每个函数基本上都会有几十次的调用, 你需要耐心地去分析每一次的调用, 找出所有可能存在漏洞的地方, 然后一级一级的向上跟踪.

而对于xss的利用, 你只需要发挥你的想象力.

评论

国光2016-05-08 16:20:57

给跪了!

hei_yu_fa2016-03-31 13:00:14

我要把乌云从头到尾翻个遍,你这篇有点叼啊...

进击的小白2016-01-21 22:08:47

大神

枯荣2015-08-05 17:05:23

其实不敢留名 因为有些没懂

ckaexn2015-08-05 12:32:04

跟大神学习

胡小树2015-02-04 23:51:30

多年以后,再看大神的漏洞还是很经典

RickGray2014-07-10 23:44:29

又看了一次,还是很经典

triangle2014-06-24 11:28:42

不错,mark

juuxdd2014-06-20 08:32:04

同求!

juuxdd2014-06-20 08:31:23

谢谢

小贱人2014-04-28 14:44:28

mark

小贱人2014-03-26 12:45:05

学习了

erevus2013-06-03 22:57:31

我看到了好多美刀 美刀啊啊啊

MEng2013-05-31 10:59:47

先收藏了

clozure2013-01-15 00:09:04

碉堡,学习

1ee2013-01-07 17:42:48

必须收藏!!!

rootkit2013-01-07 11:39:48

学习了!!

紫梦芊2013-01-06 23:15:21

分析相当到位啊

p.z2013-01-06 20:01:44

audio.swf文件地址:http://swfpoc.appspot.com/vul/audio.swf

Blackeagle2013-01-06 19:56:58

过来围观下

open2013-01-06 19:40:19

牛B!我最膜拜这句

infper2013-01-06 17:56:42

膜拜!畅快淋漓!

wanglaojiu2013-01-06 16:57:25

好详细,第一次见。

龙臣2013-01-06 12:01:21

学网易??

redrain有节操2013-01-06 01:57:36

碉堡了,mark

GaRY2013-01-04 12:28:12

我操,碉堡

imlonghao2012-12-29 16:48:22

顶一个,看看看到有这个站~

shine2012-12-29 15:25:55

很详细了,学习了!

黄小昏2012-12-29 15:04:42

mark 学习了,精华就是猛

Xhm1n92012-12-29 11:54:38

学习了

se55i0n2012-12-29 11:50:08

经典就不要藏着掖着了呀~~

w5r22012-12-29 10:55:18

有就拿上来看看撒。

Adra1n2012-12-29 09:40:11

牛叉啊!!

xsser2012-12-28 23:20:17

貌似还有其它经典的......

gainover2012-12-28 23:18:04

广告位招租。