2

一个可大规模悄无声息窃取淘宝/支付宝账号与密码的漏洞 -(埋雷式攻击附带视频演示)...

 6 years ago
source link:
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一个可大规模悄无声息窃取淘宝/支付宝账号与密码的漏洞 -(埋雷式攻击附带视频演示)

0x00 说在前面的话


下面是一段如何利用flash xss rookit漏洞来窃取淘宝/支付宝帐号密码的演示视频,当前漏洞已经修复

由于视频网站上传的均不给通过,只好给出一个URL了:

http://static.wooyun.org/files/taobaoxss.swf

网速好的可以下载并观看清晰版本:

http://static.wooyun.org/files/taobaoxss.wmv

2月底我们PKAV团队在乌云(http://www.wooyun.org/index.php)上向阿里(淘宝/支付宝)报告了该漏洞,漏洞的标题看起来有点恐怖,但它绝对不是“标题党”。 乌云的漏洞审核人员在看了漏洞中的视频演示后,为其附加了一个生动又有一点霸气的名字:"埋雷式攻击"。

为何叫埋雷呢?本文中也会给出具体的解释。

当时阿里推出了5W的奖励计划,有人问我这个漏洞为什么不上报asrc(阿里巴巴漏洞应急响应中心),报乌云没奖励的。

我给出的答复是:“ 思路灵感都是乌云来的,自当报还之”。

本人乃至本团队,还是更喜欢并赞同乌云上这种透明的机制,从我们自身角度来讲能学习到不少,从用户角度来讲,至少可以让用户知道发生过什么,怎么保护自己 (试想,如果本文中的漏洞曾被黑客拿来窃取过用户帐号与密码,然后某天厂商修复了,用户并不知道曾经有这个漏洞发生过,也不知道自己密码是如何被盗的)。

从厂商角度来讲:Oh shit! 不要拿漏洞恐吓用户!

下面带大家一起来感受一下,这个来自乌云,又回归乌云的漏洞历程。

0x01 第一次漏洞报告


这个有意思的漏洞最初在乌云上由白帽子neobyte报告( WooYun: 一个flash的0day导致的淘宝网存储xss(可形成永久后门) ),该漏洞描述了在一些FLASH应用中,因使用Local Shared Object (以下简称LSO)与ExternalInterface.addCallback而导致的一类XSS漏洞。由于此前并没有见到类似的报告,从开发人员角度来说,并不会 十分去注意并防御此类漏洞。我们来看看,在开发人员的角度,通过js与as的交互来实现读取FLASH本地缓存功能的流程图:

enter image description here

在这个示意图中,红色的箭头表示函数的调用,蓝色的箭头表示用户数据的流向。普通的开发人员仅仅在意功能是否完整的实现;带有一些安全意识的开发人员会注意到,jsGetData所获得的数据需要经过过滤再进入DOM, 如果没有考虑这点会造成什么安全问题呢?乌云上的p.z大神也给我们举了一个好例子( WooYun: 淘宝某处存储型XSS漏洞 ); 但是现在,我们将会遇到一个最糟糕的情况:“不论你是否是一个有安全意识的程序员,你都十分可能掉入这个坑中!”。

这是一个什么样的坑呢?

我们得先更深入了解下javascript调用FLASH所提供的函数接口时,到底做了哪些事情。下图描述了当我们调用jsGetData函数来获取FLASH本地缓存数据的调用过程:

enter image description here

上面是一个正常获取数据的流程,假设LSO中存放的数据是AAAAAAAAAAAA,最终,我们的data变量的值也为AAAAAAAAAAAA,当然,这是理想状态下的情况。

如果你对DOM XSS有一定了解,也知道eval是多么evil!(不了解但有兴趣的参见本人马甲贴: WooYun: [腾讯实例教程] 那些年我们一起学XSS - 10. Dom Xss进阶 [邂逅eval] ),那么你一定不难想到下面的做法:将存储在LSO中的数据做以下改变:

AAAAAAAAAAAA 返回数据过程中会执行: eval('"AAAAAAAAAAAA"');

如果我们将存储的数据更换为:

#!javascript
AAAAAA";alert(1);//AAAAAA 

返回数据将执行:

#!javascript
eval('"AAAAAA";alert(1);//AAAAAA"');

可以看到,我们在eval的数据中插入了一个alert(1),当eval执行时,就会执行弹窗操作!(你也可以打开F12的console,然后复制上面的代码运行试一试!

然而,上面只是我们假想的情况。实际上FLASH会对返回数据进行转义操作,将双引号 (") 转义为反斜线+双引号 (\"),即我们更改存储的数据后,实际执行的是下面的代码:

#!javascript
eval('"AAAAAA\";alert(1);//AAAAAA"');

正是这个转义,使得我们上面的想法泡汤了。但是,别灰心,FLASH对返回数据的过滤是存在问题的,虽然过滤了双引号 ("),但是FLASH并没有过滤反斜线 (\),我们只需将上面的代码稍加修改,如下:

#!javascript
AAAAAA\";alert(1);//AAAAAA 

返回数据将会是以下的情况:

#!javascript
"AAAAAA\\";alert(1);//AAAAAA"

随后返回内容进入eval被当作脚本执行。

eval('返回内容');

其中双引号 (")被转义为反斜线+双引号("),但是该反斜线却被我们添加的反斜线所转义。

//如果你想在控制台测试效果,可以执行以下代码

#!js
eval('"AAAAAA\\\\";alert(1);//AAAAAA"');

//-- 此处由 @piaca 指出并更正描述,由于实际内容写成字符串时,反斜线需要被写成转义形式。

小总结:到了这里,我们可以知道的是,如果我们可以在LSO中存储恶意数据,当HTML页面中通过javascript调用jsGetData函数,继而通过actionscript中的asGetData函数, 从Flash本地存储中读取该恶意数据时,就会触发我们的恶意代码。

接下来的问题,我们如何往LSO中添加我们的恶意数据呢?最暴力的方法是,抢你电脑,拔你网线,拆你硬盘,而后改之!,&@……!%&!%@ ... 温柔,一定要温柔!

在我们的第一个图中,开发人员同样为我们实现了

jsSetData -> asSetData -> LSO

的流程,也就是说,我们可以通过Javascript调用jsSetData来实现修改LSO中的数据。从功能角度上是这么设计的,然而出于安全考虑,FLASH所提供的外部函数接口,并不是什么时候都可以调用的,在默认情况下,只有http://A.com的网页文件才能调用http://A.com下的FLASH文件所提供的函数接口,如下图:

enter image description here

这样看来,其实在默认的安全设置下,我们并没有直接的办法来修改数据。此外,每个域名下FLASH文件所使用的缓存是独立的,因而正常情况下,我们也无法使用http://B.com的FLASH文件来修改http://A.com下的FLASH缓存(不排除有猥琐的办法哦~)。

然而,这种”js读写FLASH缓存“的功能模块,天生会要求有一种特性,就是写一次,四处皆可用,从开发的角度来讲,这是天经地义的事情,方便,很方便。于是乎,开发人员在代码中加上了下面这么一句:

#!javascript
Security.allowDomain("*");

这一句代码的具体作用,可参见官方手册(Flash CS4 Professional ActionScript 2.0)),简单来说,这一句话,打破了我们上面所述的FLASH默认安全设置,使得任何域名下的网页均可以调用A.com下FLASH文件所提供的addCallback接口。

这样一来,我们整个漏洞的原理图就可以建立起来了:

enter image description here

这个图箭头比较多。当黑客创建了恶意页面后,我们以受害者作为起点,当受害者访问了恶意页面后,会执行一系列操作,从而将恶意数据存入LSO中,这个过程,我们称之为“埋雷”。哪里会被“埋雷”呢?答案是很多地方:

譬如:

低级一点的:黑客A向你发送一个链接,你点开了。

不知不觉一点的:当你到处点网页的时候!

高级一点的:你看了一篇黑客写的博文/日志,或是有黑客给你的QQ空间日志发表了一条留言或者评论,内容里被植入了XSS代码(需结合QQ的XSS漏洞),你点开了。

再高级再不知不觉一点的:某天你QQ收到了一条礼物消息(QQ空间礼物功能XSS可以攻击任意指定QQ号码用户),或者是某天突然来了一个陌生人的QQ对话框(微博上正在疯传的那个QQ客户端聊天就中的XSS(可登陆和控制他人账号))。

总之,如果是有心而为之,你!,被埋雷的可能性非常大。

当受害者被埋下雷之后,受害者再次访问目标站(比如本次漏洞中的淘宝,或者是支付宝),黑客所埋下的恶意代码就会被触发,Bomb! 雷就爆炸了!当然,实际上黑客不大可能去恶作剧式的“Bomb!"的吓你一跳,而是偷偷的,悄无声息的利用这颗无声的”雷“来窃取受害者的帐号与密码。

看我敲了这么多,发了这么多张图,你们觉得这个漏洞的潜在危害大么?反正我觉得挺大的,再来看看厂商对neobyte提交漏洞的响应:

危害等级:中

漏洞Rank:8

确认时间:2013-10-12 16:44

是的,厂商似乎没有感觉到这个漏洞的危害。

0x02 第二次漏洞报告


到了2013年10月24号,我已经可以查看neobyte所提交的漏洞详情,于是就对他所提交的这个漏洞进行了研究,由于自己对FLASH也推敲过一些,在明白成因之后,也下载了漏洞中所提到的那个存在缺陷的FLASH文件(http://acjstb.aliyun.com/actionlog/flash/JSocket.swf)。以下是对方的修复代码:

#!as3
public function getlso():String{
    var _local1:SharedObject = SharedObject.getLocal("kj");
    var _local2:RegExp = new RegExp("[a-zA-Z]");
    if (_local1.data.key == undefined){
        return ("");
    };
    if (_local2.test(_local1.data.key)){
        this.setlso("");
        return ("");
    };
    return (_local1.data.key);
}

由于业务上,淘宝此处存储在LSO的数据,并不需要字母,但是恶意代码中却需要使用到字母,例如:alert。所以开发人员这里加上了一个正则判断:“如果所获取的数据中存在字母,则返回空字符串”。是的,看起来是被修复了,但是依然存在问题,一来,开发人员并没有过滤掉真正导致潜在危害的反斜线(),二来,Javascript中调用函数完全可以不需要存在任何字母,纳尼??一起来看看:

#!as3
//我们可以使用以下方式创建一个函数:
new Function("alert(1)");
//也可以不要new
Function("alert(1)")()
//可以将Function转换下
"...".substr.constructor("alert(1)")()
//再转换下
"..."["substr"]["constructor"]("alert(1)")()
//字符串全部转义
"..."["\163\165\142\163\164\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("\141\154\145\162\164\50\61\51")()

这样我们就实现了不需要字母即可执行alert(1)的目的。再利用在上一小节中所说的恶意代码构造技巧,我们只需将AAAAAAAAAAAAAA替换为以下代码:

#!javascript
...\"["\163\165\142\163\164\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("\141\154\145\162\164\50\61\51")()//..

漏洞就这样再次出现了,虽然当时已经想到这个漏洞用来钓鱼的危害挺大,但是并没有动力去做一个钓鱼演示,并且漏洞刚被阿里修复不久,只是出于技术上的好意,想提醒开发人员完美的修复。于是,我“不好意思”的登上了我另外一个马甲号,发了一个续集: WooYun: 一个flash的0day导致的淘宝网存储xss 【续集】 。当时我给漏洞自评了8分,因为漏洞的原作者neobyte也只要了8分,我也没敢多要。但是,对方竟然只给了我5分:

危害等级:中

漏洞Rank:5

确认时间:2013-10-24 14:19

0x03 第三次漏洞报告


恩,不得不说,为什么还会有第三次呢?主要是因为上面这个5分!个人觉得,如果一个漏洞被修复后,又被绕过,这是更加严重的情况,怎么说也不应该给5分,起码也得6分是不!。这个5分大大刺激了我的“报复欲”,让我想再次绕过他们的修复方案,然后做一个大规模的钓鱼演示,让他们知道这个绝对不能是5分!!!。但是呢,这一次他们的修复方案并没有好的绕过方法! 虽然如此,隔三差五的,我还是会去研究研究,不怕贼偷,就怕贼惦记.......,直到有一天,我发现FLASH中的修复方案竟然被换掉了,并且,第一眼,我就看出这个换掉的方案又出现问题了。新的修复代码如下:

#!javascript
private function getlso():String{
    var _local1:SharedObject = SharedObject.getLocal("kj");
    var _local2:RegExp = new RegExp("[\\({]");
    if (_local1.data.key == undefined){
        return ("");
    };
    if (_local2.test(_local1.data.key)){
        this.setlso("");
        return ("");
    };
    return (_local1.data.key);
}

可以看到,正则表达式 _local2 仅仅过滤了 ( 和 { ,而前面也已提到,正确的过滤方法应该是过滤 \ 。经过前面2轮的洗礼,我觉得安全人员应该是知道\的危害了,为什么还会出现过滤失误呢?我在漏洞描述中也已经给出了以下猜测,后确认,答案是B。毕竟我也是写了很多年前端的,RegExp这个坑在我曾经也数次掉入。

A. 响应漏洞的同学并没看到我的修复建议并转告开发同学。

B. 开发的同学,认为 [\\({] 是 \ , ( 和 } 的集合, 而实际上,RegExp方式创建正则表达式时,需要写成 [\\\\({] ,原因是:\在字符串里是转义符,在正则里也是转义符。

当然,还需要注意的是,这个正则,不允许(和{,那么还可以执行javascript代码吗?答案是可以的,如下:

#!javascript
location.href="javascript:alert%28129";

使用location.href来执行js,并且将其中的括号与花括号进行URL编码。

嗯,这个漏洞就这样再次出现了,这一次我可不想再得到那可怜的5分!于是花了时间写了一个真实可利用的代码,并录制了本文最开始的视频,撰写了漏洞报告( WooYun: 一个可大规模悄无声息窃取淘宝/支付宝账号与密码的漏洞 -(埋雷式攻击附带视频演示) )。阿里这次给出了15分,还是没有给出我心中的20分。怎么说呢,从用户角度来讲,它的危害肯定是值20分的,但是厂商并不总是站在用户考虑问题的。

危害等级:高

漏洞Rank:15

确认时间:2014-02-26 10:25

0x04 对于开发人员


注意addCallback: 在Flash中使用addCallback向外部提供函数接口时,如果as中对应函数的返回值是String, Array, Object等类型,并且返回数据中有用户可控的数据时,建议对返回值进行过滤操作,将反斜线()过滤掉。如果没有进行过滤的话,则可能出现与( WooYun: QQ空间某功能缺陷导致日志存储型XSS - 14 )相类似的案例。这里我就不给出具体的过滤代码了,相信一般的开发人员的as都比我写的好,以下是伪代码:

#!javascript
function asFunction():String{
     var data:String="xxx某些用户可控数据xxx";
     var obj:Object={"name":"用户可控数据"};
     ....
     data = 过滤(data);
     obj = 遍历过滤(obj); 

     return data; 或 return obj;
}

勿滥用*|*Security.allowDomain("*"): **不是非常、极其、十分需要的情况下,建议将allowDomain限定在指定的域名下面,而不要为了方便而使用*。因为使用*,可能还会导致其它安全问题。

0x05 对于普通用户


由于这类漏洞十分隐蔽,目前应该没有安全软件能防御此类攻击。鉴于绝大部分厂商并不会“举一反三”的修复同类问题,我们不能完全指望厂商,安全还得靠自己。如果我们尽可能的定期清理Flash player缓存,理论上会大大降低此类漏洞造成的危害。

Flash的本地缓存一般存放在一个叫做 #SharedObjects的目录中,如果有使用Everything这款搜索工具的网友,可以使用Everything查找所有#SharedObjects目录,然后这些目录下的目录(8位的数字/字母组成)下的内容进行删除处理。(当然,删除#SharedObjects目录下数据,可能导致一些FLASH游戏或者FLASH应用的数据需要重新下载,但是相比之下,安全更重要。)

如果你没有使用Everything,下表列出了IEchrome中Flash的缓存目录所在,找到对应目录进行清理即可,其它浏览器用户,如360***|||||*,搜狗,可自行搜索C盘下SharedObjects目录进行相应删除操作。

IE浏览器

C:\Documents and Settings\用户名 \Application Data\Macromedia\Flash Player\#SharedObjects (Win XP 系统)

C:\Users\用户名\AppData\Roaming\Macromedia\Flash Player\#SharedObjects\ (Win 7)

Chrome

C:\Documents and Settings\用户名 \Local Settings\Application Data\Google\Chrome\User Data\【用户目录】\Pepper Data\Shockwave Flash\WritableRoot\#SharedObjects (Win XP 系统)

C:\Users\用户名 \AppData\Local\Google\Chrome\User Data\【用户目录】\Pepper Data\Shockwave Flash\WritableRoot\#SharedObjects (Win 7)

*【用户目录】可能是Profile+数字 或者 Default

常见的“卫士”软件,自带了垃圾清理功能,但是可能在默认设置中,并未开启清理FLASH缓存功能,所以需要用户手动勾选“清理Flash player缓存”。这里以金山卫士为例:清理垃圾->其它常用软件产生的垃圾文件->Flash player缓存。 但是该方法也许并不会完全清理干净,例如:并不会清理chrome浏览器的flash缓存,因此还是建议使用以上方法手工清理。

0x06 说在后面的话


这个漏洞虽然标题是在说“窃取淘宝/支付宝的帐号与密码”,有些人觉得反正不知道支付密码,不要紧!但是这个漏洞所造成的潜在危害,并不是只有淘宝/支付宝帐号与密码那么简单。试想,如果在淘宝的登录页面上再结合一个QQ的xss漏洞,我们可以得到什么信息呢?淘宝/支付宝帐号、密码,QQ帐号及QQ相关信息(关于QQ的XSS可以做什么,参见乌云一哥jannock的  WooYun: 跨站脚本-可以让战场离得更远(浅谈腾讯架构缺陷) ,这个2012年时候的情况,现在已经有所好转,但是当前状况似乎依然不是太好)。此外,由于淘宝/支付宝这类涉及现金的帐号,密码等级往往十分高,可能会出现支付宝登录密码同样是QQ密码的情况,所以这类密码一旦泄漏,可能会涉及后续更严重的信息泄漏。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK