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
元素,然后在a1
、a2
、a3
……中通过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图像在被上传到服务端前都必须经过过滤处理,可以采取如下操作:
- 限制危险标签,比如
script
、foreignObject
等。 - 限制通过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)