SVG在Web攻击中的应用

 

0x00 前言

在过去几周中,FortiGuard Labs一直在研究带有SVG(Scalable Vector Graphics)图像的Web应用。根据研究结果,我们找到了Web应用中的一些常见问题。在本文中,我们简要介绍了SVG的特点以及针对SVG图像的常见攻击面。

根据之前的研究结果,我们梳理了一些常见的SVG攻击方式,如下所示:

  • 跨站脚本(Cross-Site Scripting)
  • HTML注入
  • XML实体:“Billion Laughs”攻击(针对XML文档解析器的一种DoS攻击)
  • DoS(拒绝服务):新型SVG “Billion Laughs”攻击。

 

0x01 SVG简介

SVG的全称为 Scalable Vector Graphics(可缩放矢量图),是一种基于XML的二维矢量图格式,支持交互性及动画展示。SVG图像及具体行为由XML文本文件定义,可以通过任何文本编辑器以及绘图软件来创建并编辑。目前所有主流web浏览器都支持渲染SVG图像。

来观察一个示例,更好理解SVG图像。如下图所示,我们编写了一些代码来渲染SVG图像:

图1. simple.svg代码片段

将该图像保存为simple.svg,然后直接打开,或者将其包含在img/image/object/embed HTML标签中,如下图所示:

图2. 通过代码渲染图像

图1代码渲染生成的图像如图2所示,这是rect元素,浏览器会在x, y (100, 100)(即宽度和高度)位置渲染一个红色矩形。

 

0x02 使用SVG的攻击场景

虽然SVG提供了较大的灵活性,可以方便创建更多的动态web内容,但同时也引入了一些安全风险。在下文中,我们将讨论一些常见的攻击向量,我们在互联网上的一些主流站点上都观察到过这些攻击方式。

跨站脚本

我们可以通过脚本方式来访问并修改SVG文档的任何内容,这与HTML操作方式类似。默认的脚本语言为ECMAScript(与JavaScript密切相关),每个SVG元素及属性都对应已定义的DOM(Document Object Model,文档对象模型)对象。相关脚本被封装在<script>元素中。

这意味着如果web服务器允许用户上传任意SVG图像,就存在XSS(跨站脚本)安全风险。如下所示,我们将脚本存放在图像中:

图3. xss.svg代码片段

将该图像保存为xss.svg,然后直接打开,如下图所示:

图4. 直接访问该文件触发XSS

如果将该文件链接到某个HTML页面,访问该页面也可以触发,如下图所示:

图5. 通过链接文件触发XSS

JavaScript代码会在浏览器上下文中执行,这意味着攻击者可以使用该文件执行恶意行为,比如窃取用户隐私信息等。

HTML注入

在某些情况下,XSS payload会被服务端过滤,然而我们依然能够通过SVG图像的特定功能来注入HTML代码。如前文所述,SVG是基于XML的一种矢量图,因此我们无法简单将HTML内容放入其中,不然会破坏XML的语法。

为了避免这种情况,SVG提供了一个foreignObject元素,可以用来包含来自其他XML命名空间的元素。在浏览器上下文中,这部分数据很可能采用(X)HTML形式。

来看一下html.svg图像:

图6. html.svg代码片段

当我们在foreignObject内添加一个body标签以及XHTML命名空间时,可以使用xmlns属性来声明命名空间。采用这种方式,浏览器会将body标签及其所有子标签解析为属于XHTML的元素。因此,我们可以将来自SVG的任意XHTML代码渲染到页面中:

图7. HTML注入漏洞

这种方式可以运行任意HTML代码,意味着我们可以简单从SVG图像中发起类似钓鱼、绕过同源策略、CSRF之类的攻击。

XML实体:Billion Laughs Attack

由于SVG是基于XML的矢量图,因此可以支持Entity(实体)功能。Entity可以用来定义特殊字符的快捷方式,也可以声明成内部或外部实体。

我们可以通过如下方式声明内部Entity

<!ENTITY entity-name "entity-value">

通过如下方式声明外部Entity

<!ENTITY entity-name SYSTEM "URI/URL">

如果解析文件的XML解析器存在脆弱性,那么我们就可以滥用外部Entity功能来泄露内部数据。由于现在大家主要使用的都是现代浏览器,因此我们假设可用的解析器都经过fuzzer的严格测试,因此没那么容易被攻击。在这个前提下,这里我们主要讨论如何滥用内部Entity

entity.svg的内部实现如下所示:

图8. entity.svg代码片段

如上图所示,我们在第2行定义lab这个Entity,然后在SVG元素中调用该实体。结果如图9所示:

图9. lab实体被加载到页面

一切非常顺利,来尝试另一个例子:entity_2.svg,如下图所示:

图10. entity_2.svg代码片段

结果如下:

图11. lab2实体被加载到页面

如上图所示,这里的文本内容被重复渲染,这表明我们可以使用Entity标签发起“ Billion Laughs ”攻击。

“ Billion Laughs ”攻击是一种DoS(拒绝服务)攻击,目标是XML文档解析器。这种攻击也被称之为XML炸弹或者指数实体攻击。

图12. billion_laughs.svg代码片段

我们的浏览器在解析这个 billion_laughs.svg数据时,只花了4~5秒就能正常响应。这是因为大多数现代浏览器已经能够能应付这种攻击,可以在渲染过程中解决该问题,因此不会造成安全风险。

拒绝服务:新型SVG “Billion Laughs”攻击

在上一节中,我们发现“ Billion Laughs ”攻击可以延缓浏览器的处理速度,浏览器需要4~5秒才能应付该攻击。不幸的是,攻击者还可以通过SVG图像,发起另一种“ Billion Laughs ”攻击,绕过这些防御措施。

这一次我们使用xlink:href来代替XML Entity。来看一下 xlink_laughs.svg所使用的payload:

图13. xlink_laughs.svg代码片段

xlink:href属性以IRI(国际资源标识)方式定义了对某个资源的引用,该链接的具体含义需根据使用该链接的每个元素的上下文来决定。

<use>元素从SVG文档中获取节点,然后将其复制到其他位置。

我们现在a0中定义circle元素,然后在a1a2a3……中通过xlink:href属性调用<use>元素,通过这种方式反复克隆circle。结果如下图所示:

图14. 在解析恶意SVG时,通过xlink:href发起“ Billion Laugh”攻击

需要注意的是,在最坏的情况下,大多数现代浏览器在尝试解析网站上的这张SVG图像时可能会发生崩溃,或者至少会出现无响应情况。

有趣的是,我们在测试某些开源SVG/XML过滤器时,发现这些过滤器并不能正确捕捉到图13所示的SVG图像。因此,这种错误格式的SVG图像也可能造成DoS效果。

 

0x03 总结

SVG图像更像HTML,而不单单是一张简单的图像。因此,我们建议web开发者尽可能不要以对象或者iframe形式加载任何SVG。Web管理员同样应当限制可以上传到站点的文件类型。

此外,任何不可信的SVG图像在被上传到服务端前都必须经过过滤处理,可以采取如下操作:

  • 限制危险标签,比如scriptforeignObject等。
  • 限制通过SVG图像的外部链接加载资源。
  • 限制SVG图像内的扩展逻辑。

我们使用一些浏览器来直接打开这些恶意SVG文件,对比结果如下图所示:

大家可以访问我们的Github仓库下载本文使用的SVG样本。

 

0x04 参考资料

[1] W3C, “Scalable Vector Graphics” https://www.w3.org/TR/SVG2/ (02 September, 2019)
[2] OWASP, “The Image that called me” https://www.owasp.org/images/0/03/Mario_Heiderich_OWASP_Sweden_The_image_that_called_me.pdf (02 September, 2019)
[3] Blackhat, “Exploiting Browsers without Image Parsing Bugs” https://www.blackhat.com/docs/us-14/materials/us-14-DeGraaf-SVG-Exploiting-Browsers-Without-Image-Parsing-Bugs.pdf (02 September, 2019)

(完)