Instagram应用代码执行漏洞详解(四)

  • Instagram应用代码执行漏洞详解(四)已关闭评论
  • 12 views
  • A+

在上一篇文章中,为读者介绍Mozjpeg内部的内存管理器以及相关堆布局等知识。在本文中,我们将综合上面的知识点,进一步探讨该漏洞的可利用性。

把所有的东西放到一起

现在,我们获得了一个受控的函数调用。对于一个可靠的漏洞来说,现在所缺少的就是将执行流从gadget重定向到stack pivot,并构建一个ROP堆栈。

现在,我们需要把所有的东西放在一起,(1)构建一个具有畸形尺寸的图像,(2)触发漏洞,然后(3)复制受控payload的副本,(4)将执行权转移到我们控制的地址。

我们需要用我们的受控数据生成一个做过手脚的JPEG。因此,我们的下一步是确定Mozjpeg平台到底支持什么样的图像格式。我们可以从下面的代码中找到答案。其中,out_color_space表示根据图像格式确定的每个像素的位数。
```
switch (cinfo->out_color_space) {
case JCS_GRAYSCALE:
cinfo->out_color_components = 1;
Break;
case JCS_RGB:
case JCS_EXT_RGB:
case JCS_EXT_RGBX:
case JCS_EXT_BGR:
case JCS_EXT_BGRX:
case JCS_EXT_XBGR:
case JCS_EXT_XRGB:
case JCS_EXT_RGBA:
case JCS_EXT_BGRA:
case JCS_EXT_ABGR:
case JCS_EXT_ARGB:
cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space];
Break;
case JCS_YCbCr:
case JCS_RGB565:
cinfo->out_color_components = 3;
break;
case JCS_CMYK:
case JCS_YCCK:
cinfo->out_color_components = 4;
break;
default:

cinfo->out_color_components = cinfo->num_components;
Break;

```

我们使用了一个简单的Python库PIL来构造一个RGB BMP文件。我们选择了大家所熟知的RGB格式,并将“AAA”作为payload填充其中。这个文件是我们用来创建恶意压缩JPEG的基准图像格式。
```
from PIL import Image
img = Image.new('RGB', (100, 100))
pixels = img.load()
for i in range(img.size[0]):

for j in range(img.size[1]):
pixels[i,j] = (0x41, 0x41, 0x41)

img.save('rgb100.bmp')

```

接下来,我们使用Mozjpeg项目中的cjpeg工具将bmp文件压缩成JPEG文件。
```
./cjpeg -rgb -quality 100 -fastcrush -notrellis -notrellis-dc -noovershoot -outfile rgb100.jpg rgb100.bmp

```

接下来,我们对压缩后的输出文件进行测试,以检验我们的假设。我们知道,RGB格式的每个像素需要占用3个字节。

我们发现,代码确实能够正确地设置cinfo->out_color_space = 0x2(JCS_RGB)。然而,当我们检查受控的内存分配时,我们看到作为整数溢出一部分的height和width参数仍然被等于4的out_color_components相乘,即使我们开始使用的是RGB格式,其每个像素使用3×8比特。看来Mozjpeg更喜欢将我们的图像转换为每像素4×8-bits的格式。

于是我们转向Mozjpeg平台支持的4×8位像素格式,并且CMYK格式也符合该标准。我们使用CMYK格式作为基础图像,因为这样的话,我们能够完全控制所有4个字节。在这里,我们用“AAAA”作为payload填充到了该图像中。

之后,我们将其压缩为JPEG格式,并添加了触发该漏洞的长度值。让我们高兴的是,我们触发了如下所示的崩溃!
```
Thread 93 "IgExecutor #19" received signal SIGBUS, Bus error.
0xff414141ff414141 in ?? ()

```

然而,尽管我们构建了一个4×8位/像素的图像,但是受控的地址中却出现了一个奇怪的字节0xFF,并且第4个分量并不是我们的payload的一部分。

这个0xFF是什么意思?透明度!

当目前为止,通过透明色或alpha通道支持透明度的位图文件格式包括GIF、PNG、BMP、TIFF和JPEG 2000。

基于位图的图像在技术上的特点是以像素为单位的图像宽度和高度,以及每个像素的位数。

因此,我们决定使用PIL库构建一个带有受控alpha通道(0x61)的RGBA BMP格式文件。
```
from PIL import Image
img = Image.new('RGBA', (100, 100))
pixels = img.load()
for i in range(img.size[0]):

for j in range(img.size[1]):
pixels[i,j] = (0x41, 0x41, 0x41,0x61)
img.save('rgba100.bmp')

```

令人惊讶的是,我们得到的结果与使用CMYK恶意JPEG时的结果相同。尽管我们使用RGBA格式作为压缩JPEG的基础,仍然得到了作为受控地址的一部分的0xFF alpha通道,并且文件中的Alpha通道的值为(0x61)。这是咋回事呢?让我们回到代码中,来了解造成这种奇怪行为的具体原因。

我们在下面这段代码中找到了答案。

1.png
图8 如IDA的反汇编结果所示,这里将cinfo->out_color_space设置成了RGBA(0xC)

我们发现,Instagram会在调用jpeg_read_header函数之后、调用jpeg_start_decompress函数之前添加自己的const值。

我们使用了第一次测试中的RGB格式,发现Mozjpeg确实正确地设置了cinfo->out_color_space = 0x2(JCS_RGB)。但是,从Instagram的代码(见图3)我们可以看到,这个值被一个代表(JCS_EXT_RGBA)格式的const值0xc给覆盖了。

这也解释了为什么就算使用了每个像素3×8位的RGB对象,也会得到奇怪的0xFF alpha通道的原因。

深入研究代码后,我们发现Alpha通道的值(0xFF)被硬编码为const值。当Instagram将cinfo->out_color_space=0xc设置为指向(JCS_EXT_RGBA)格式时,代码会从输入的基准文件中复制3个字节,并且复制的第4个字节始终是硬编码的Alpha通道值。
```

ifdef RGB_ALPHA

  outptr[RGB_ALPHA] = 0xFF;

endif

```

现在我们把所有的东西放在一起,我们会得出这样一个结论:无论压缩后的JPEG的基准采用什么图像格式,Instagram总是把输出文件转换成一个RGBA格式的文件。

由于0xFF总是被添加到开头部分,这意味着我们可以在大端环境中达成我们的目标。

我们知道,小端(Little - endian)系统会将字的最低有效字节存储在最小的存储器地址中。由于我们面对的是一个小端系统,所以alpha通道值总是作为受控地址的MSB(最高有效字节)写入。由于我们试图在用户模式下利用这个漏洞,而(0xFF)值却属于内核地址空间,因此,我们是无法得逞的。

这个漏洞能够利用吗?

到目前为止,至少偷懒是不可能的了。我们可以从中得到的一个教训是,现实生活不同于CTF比赛,有时开发者设置的一个关键的常量值,从漏洞利用的角度看来,简直就是灭霸的一个响指!

让我们回顾一下Mozilla基金会主网站上关于Mozjpeg的介绍:

“MozJPEG的唯一目的就是减少在网络上传输的JPEG文件的大小。”

从我们看到的情况来看,我们每上传一张图片,Instagram就会增加25%的内存使用量!而每天上传的图片数量大约一亿张左右!

1.png

引用Halvar Flake在上届OffisiveCon大会演讲中的一句话:

"在计算领域,真正彻底了解系统的人只有攻击者!其他所有人,通常只是受雇了解被要求的那些部分。"

如今,Facebook已经修补了这个漏洞,所以我们停止了该漏洞利用的方法的研究,尽管我们还没有完全解决利用方法的问题。

我们还有3个字节的覆盖空间,理论上我们可以投入更多的时间去寻找更多有用的原语,以帮助我们利用这个漏洞。然而,我们觉得我们做得已经够多了,我们已经公开了我们想要传达的重要观点。

在谈论Mozjpeg时,Instagram上的Mozjpeg项目只是冰山一角。这个基于Mozilla的项目仍然被广泛地应用于网络上的许多其他项目,尤其是Firefox,它也被广泛地应用于不同的流行的开源项目中,如sharp和libvips项目(仅在Github平台上,它们的星星加起来就超过了2万颗)。

结论与建议

我们在本文中描述了作为第三方库的图像解析代码最终如何成为Instagram大型系统的最薄弱环节的。通过对公开代码进行模糊测试,我们发现了一些新漏洞,不过这些漏洞已得到修复。如果下足功夫,攻击者很可能在零点击攻击情形下将这些漏洞用于RCE。不幸的是,将来也可能会存在或将引入其他漏洞。因此,绝对有必需在操作系统库和第三方库中对相关的媒体格式解析代码进行持续的模糊测试。我们还建议通过限制接收器所支持的图像格式来减少攻击面。

许多独立的安全研究人员以及国家资助的安全研究人员都对该领域进行了大量的研究。媒体格式解析仍然是一个重要的安全研究问题。另请参阅其他研究人员和供应商的相关安全建议:

  • https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/
  • https://www.facebook.com/security/advisories/cve-2019-11931
  • J00ru 0-click RCE on Samsung Galaxy 10
  • https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html

Facebook的安全公告将该漏洞描述为“整数溢出导致堆缓冲区溢出——当Instagram for Android尝试上传具有精心设计的尺寸的图像时,可能会出现大型堆溢出。该漏洞会影响128.0.0.26.128之前的版本。”

我们负责任地向Facebook披露了该漏洞,Facebook于(2020年2月10日)发布了相应的安全补丁。Facebook承认了该漏洞,并将其编号为CVE-2020-1895。该漏洞已针对32位和64位版本的Instagram应用进行了相应的测试。

参考文献

https://phrack.org/issues/68/10.html

https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html

https://blog.nsogroup.com/a-tale-of-two-mallocs-on-android-libc-allocators-part-3-exploitation/

https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/

https://saaramar.github.io/str_repeat_exploit/

https://googleprojectzero.blogspot.com/2015/09/stagefrightened.html

https://github.com/NorthBit/Metaphor

https://bugs.chromium.org/p/project-zero/issues/detail?id=2002

https://libjpeg-turbo.org/About/Mozjpeg

Many thanks to my colleagues Eyal Itkin (@EyalItkin), Oleg Ilushin, Omri Herscovici (@omriher) for their help in this research.

原文地址:https://research.checkpoint.com/2020/instagram_rce-code-execution-vulnerability-in-instagram-app-for-android-and-ios/

相关推荐: CTF PWN之精确覆盖变量数据

刚开始接触pwn的朋友在做pwn练习时可能会有这样的疑问,怎么做到精确覆盖变量数据呢? 我们做pwn练习之前需要先知道:命令行参数C语言的main函数拥有两个参数,为int类型的argc参数,以及char**类型argv参数。其中argc参数的值表示命令行参数…