研究Google Scholar上的多态图像XSS攻击
2020年4月30日 - 作者:Lorenzo Stella
几个月前,我在Google Scholar上发现了一个有趣的设计模式。该Web应用程序的多个屏幕通过结合使用location.hash参数和XHR从相对URI获取模板片段,并在页面上未经转义地渲染它们。
这本身并不危险,除非平台允许用户上传任意内容并从同一源提供服务,而Google Scholar由于其图像上传功能确实如此。虽然任何有经验的渗透测试人员都会认为利用该问题很简单,但Scholar的图像处理后端会对上传的图像应用不同的转换(即剥离元数据并重新处理图片)。在报告漏洞时,谷歌的VRP团队认为上传携带有效XSS载荷的多态图像是不可能的,而是要求提供概念证明(PoC)。
鉴于这种技术的年代,我首先回顾了所有过去“众所周知”的生成多态图片的技术,然后开发了一个测试套件来研究一些最流行的图像处理库(如Imagemagick、GraphicsMagick、Libvips)的行为。这一努力导致了一些有趣的发现。其中一些方法还可以用于隐藏Web shell或JavaScript内容,以绕过“self”CSP指令。
EXIF中的载荷
最简单的方法是将我们的载荷嵌入图像的元数据中。对于JPEG/JFIF,这些元数据存储在应用程序特定的标记(称为APPX)中,但大多数图像库不会考虑它们。Exiftool是一个流行的编辑这些条目的工具,但您可能会发现在某些情况下字符会被实体转义,因此我选择手动插入它们。
希望Google Scholar保留一些白名单的EXIF,我创建了一个包含1.2k个常见EXIF标签的图像,包括CIPA标准和非标准标签。
虽然这在我的情况下没有奏效,但一些EXIF条目至今仍保留在许多流行的Web平台中。在测试的大多数图像库中,从PNG转换为PNG时,PNG元数据总是被保留,而从PNG转换为JPG时,它们总是丢失。
载荷附加在图像末尾(JPG的0xFFD9之后或PNG的IEND之后)
这种技术只有在不对上传的图像进行任何转换时才有效,因为只有图像内容被处理。
顾名思义,这个技巧涉及将JavaScript载荷附加在图像格式的末尾。
PNG的iDAT中的载荷
在PNG中,iDAT块存储像素信息。根据应用的转换,您可能能够直接将原始载荷插入iDAT块,或者尝试绕过调整大小和重新采样操作。Google Scholar只生成JPG图片,因此我无法利用这种技术。
JPG的ECS中的载荷
在JFIF标准中,熵编码数据段(ECS)包含代表图像数据的最小编码单元(MCU)的原始霍夫曼压缩比特流输出。理论上,可以将我们的载荷定位在这个段中,但不能保证我们的载荷能在服务器上图像库应用的转换中存活下来。创建一个抵抗库转换的JPG图像是一个试错的过程。
作为起点,我制作了一个具有与转换后图像相同质量因子的“基础”图像。为此,我最终使用了这个具有0长度字符串EXIF的图像。尽管将载荷定位在节开头的可变偏移处没有奏效,但我发现当Google Scholar处理时,图像ECS节的前几个字节如果被0x00和0x14字节的模式分隔,就会被保留。
从这里开始,我花了一些时间找到允许载荷在转换中存活的正确字节序列,因为大多数用户代理不容许页面脚本标签定义中的低值字节。对于任何感兴趣的人,我们已经提供了嵌入onclick和mouseover事件的图像。我们的图像库测试套件可在Github上找到:doyensec/StandardizedImageProcessingTest。
时间线
[2019-09-28] 报告给Google VRP
[2019-09-30] Google的VRP要求PoC
[2019-10-04] 提供PoC #1
[2019-10-10] Google的VRP要求不同的PoC载荷
[2019-10-11] 提供PoC #2
[2019-11-05] Google的VRP确认2个端点的问题,奖励6267.40美元
[2019-11-19] Google的VRP发现另一个使用相同技术的XSS,额外奖励3133.70美元