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

admin 2021年4月15日01:05:05Instagram应用代码执行漏洞详解(一)已关闭评论183 views字数 5719阅读19分3秒阅读模式

背景知识

Instagram是当下最受欢迎的社交媒体平台之一,用户每天在该平台上传超过1亿多张照片。为此,我们决定对Android和iOS操作系统上的Instagram应用进行安全审计,结果发现了一个高危漏洞:攻击者可以利用它在受害者的手机上远程执行代码。

我们这次研究采用的方法是检查Instagram使用的第三方项目。

如今,几乎所有软件开发者都会在软件中利用开源项目。因此,我们就沿着这种思路,考察Instagram使用的开源项目,最终在Mozjpeg中发现了一个安全漏洞。而Mozjpeg就是一个JPEG格式解码器的开源项目。

在我们下面描述的攻击场景中,攻击者只需通过电子邮件、WhatsApp或其他媒体交换平台向受害者发送一张图片即可。当受害者打开Instagram应用时,该漏洞就会被触发。

我们都知道,如今即使是最大的公司也依赖于公共开源项目,而且这些项目几乎无需修改就能集成到他们的应用程序中。

大多数使用第三方开源项目的公司都会声明这一点,但并不是所有的库都会出现在应用程序的“About”页面中。要了解Instagram应用程序所涉及的所有库的最好方法,就是亲自打开该应用的lib-superpack-zstd文件夹进行查看。

Instagram应用代码执行漏洞详解(一)
::: hljs-center

图1 Instagram使用的共享对象

:::

在下图中,你可以看到使用Instagram上传图片时,会加载三个共享对象:libfb_mozjpeg.so、libjpegutils_moz.so和libcj_moz.so。

Instagram应用代码执行漏洞详解(一)
::: hljs-center

图2 Mozjpeg的共享对象

:::

其中,后缀“moz”是“mozjpeg(也就是Mozilla JPEG Encoder Project)”的简称,但是,这些模块到底是干什么的呢?

关于Mozjpeg

我们先来简单介绍一下JPEG格式的历史。JPEG是一种从20世纪90年代初就已经面试的图像文件格式,这种格式是基于有损压缩概念的,这意味着在压缩过程中会损失一些信息,但这种信息损失对人眼来说可以忽略不计。而Libjpeg则是Windows、Mac和Linux操作系统中内置的基线JPEG编码器,它是由一个非正式的独立小组维护的。这个库试图在编码速度、质量与文件大小之间取得平衡。

相比之下,Libjpeg-turbo则是libjpeg库的更高性能替代品,是大多数Linux发行版的默认库。这个库被设计成在编码和解码过程中使用更少的CPU时间。

2014年3月5日,Mozilla宣布了“Mozjpeg”项目,这是一个建立在libjpeg-turbo基础之上的JPEG编码器,以牺牲性能为代价,为网络图像提供更好的压缩性能。

这个开源项目是专门用于Web上的图片的。Mozilla公司在2014年推出了libjpeg-turbo,这样他们就可以专注于减少文件尺寸,以降低带宽,从而更快地加载Web图片了。

Instagram决定将mozjpeg库拆分成3个不同的共享对象:

  • libfb_mozjpeg.so:负责Mozilla特有的解压导出API。
  • libcj_moz.so:即libjeg-turbo,它负责解析图像数据。
  • libjpegutils_moz.so:两个共享对象之间的连接器。它保存JNI调用的导出API,以触发Java应用端的解压操作。

模糊测试

我们在CPR的团队建立了一个多处理器模糊测试实验室,结合Adobe Research给我们带来了惊人的结果,所以,我们决定将我们的模糊测试工作扩展到Mozjpeg库上面。

由于libjpeg-turbo库已经过了充分的模糊测试的考验,所以我们把重点放在Mozjpeg库上。

Mozilla在libjpeg-turbo的基础上增加的主要部分是压缩算法,所以,我们把目光投向了这些算法上面。

AFL是我们的首选武器,所以,我们自然要为它写一个harness。

为了写出这个harness,我们必须了解如何对Mozjpeg的解压函数进行检测。

幸运的是,我们可以通过Mozjpeg自带的示例来了解该库的用法。
```
METHODDEF(int)
do_read_JPEG_file(struct jpeg_decompress_struct cinfo, char filename)
{
struct my_error_mgr jerr;
/ More stuff /
FILE infile; / source file /
JSAMPARRAY buffer; /
Output row buffer /
int row_stride; /
physical row width in output buffer /
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %sn", filename);
return 0;
}
/
Step 1: allocate and initialize JPEG decompression object /
/
We set up the normal JPEG error routines, then override error_exit. /
cinfo->err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/
Establish the setjmp return context for my_error_exit to use. /
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(cinfo);
fclose(infile);
return 0;
}
/
Now we can initialize the JPEG decompression object. /
jpeg_create_decompress(cinfo);
/
Step 2: specify data source (eg, a file) /
jpeg_stdio_src(cinfo, infile);
/
Step 3: read file parameters with jpeg_read_header() /
(void)jpeg_read_header(cinfo, TRUE);
/
Step 4: set parameters for decompression /
/
In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
/
/
Step 5: Start decompressor /
(void)jpeg_start_decompress(cinfo);
/
JSAMPLEs per row in output buffer /
row_stride = cinfo->output_width * cinfo->output_components;
/
Make a one-row-high sample array that will go away when done with image /
buffer = (
cinfo->mem->alloc_sarray)
((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1);
/ Step 6: while (scan lines remain to be read) /
/ jpeg_read_scanlines(...); /
while (cinfo->output_scanline < cinfo->output_height) {
(void)jpeg_read_scanlines(cinfo, buffer, 1);
/ Assume put_scanline_someplace wants a pointer and sample count. /
put_scanline_someplace(buffer[0], row_stride);
}
/ Step 7: Finish decompression /
(void)jpeg_finish_decompress(cinfo);
/ Step 8: Release JPEG decompression object /
jpeg_destroy_decompress(cinfo);
fclose(infile);
return 1;
}

```

然而,为了确保我们在Mozjpeg中发现的任何崩溃都会影响Instagram本身,我们需要知道Instagram是如何将Mozjpeg集成到他们的代码中的。

幸运的是,下面你可以看到,Instagram对于该库的使用简直就是复制粘贴的典范:

::: hljs-center

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

::: hljs-center

图3 Instagram使用Mozjpeg的方式

:::

正如你所看到的,他们唯一的改动之处就是将示例代码中的put_scanline_someplace虚函数替换为使用memcpy函数的read_jpg_copy_loop函数。

我们的harness从AFL接收生成的图像文件,并将它们发送到经过封装的Mozjpeg解压缩函数。

我们只用30个CPU核心运行了一天的fuzzer,AFL就向我们通报了447个“独特的”崩溃。

在对结果进行筛选后,我们发现了一个有趣的崩溃,与JPEG的图像尺寸解析有关。该崩溃是由于越界写入导致的,我们决定对其进行深入研究。

CVE-2020-1895漏洞

实际上,存在问题的函数是read_jpg_copy_loop,它会导致解压过程中发生整数溢出。

Instagram应用代码执行漏洞详解(一)
::: hljs-center

图4 截取自IDA的read_jpg_copy_loop代码片段

:::

该存在漏洞的函数在解析JPEG图像文件时需要处理图像尺寸。下面是描述该漏洞的伪代码:
```
width = rect->right - rect->bottom;
height = rect->top - rect->left;
allocated_address = __wrap_malloc(widthheightcinfo->output_components);// <---Integer overflow
bytes_copied = 0;
while ( 1 ){
output_scanline = cinfo->output_scanline;
if ( (unsigned int)output_scanline >= cinfo->output_height )
break;
//reads one line from the file into the cinfo buffer
jpeg_read_scanlines(cinfo, line_buffer, 1);
if ( output_scanline >= Rect->left && output_scanline < Rect->top )
{
memcpy(allocated_address + bytes_copied , line_buffer, width*output_component);// <--Oops
bytes_copied += width * output_component;
}
}

```

首先,我们来了解一下这段代码的作用。

其中,_wrap_malloc函数会根据3个参数(即图形尺寸)来分配一个内存块。其中,width和height这两个参数都是从文件中解析出来的16位整数(uint16_t)。

而参数cinfo->output_component则用于指出每个像素对应于多少个字节。

这个变量的取值可能是1(灰度)、3(RGB)或4(RGB+AlphaCMYK等)。

除了width和height之外,参数output_component也完全处于攻击者的控制之下。因为它也是从文件中解析出来的,并且没有对文件中的剩余数据进行验证。

并且,__warp_malloc的预期是:其参数是通过32位寄存器进行传递的! 这意味着,如果我们能够设法让分配的内存块的大小超过(2^32)字节,就会导致整数溢出,这样的话,实际分配的内存块就会比预期的尺寸小得多。

实际上,分配的内存块的大小是通过将图像的宽度、高度和output_components相乘得到的。问题在于,软件并没有对这些尺寸没有进行相应的检测,并且完全处于攻击者的控制之下。当它们被攻击者利用时,就会导致整数溢出。

```

__wrap_malloc(width * height * cinfo->output_components);// <---- Integer overflow

```

更让攻击者喜出望外的是,这个缓冲区将会传递给memcpy函数,从而导致基于堆的缓冲区溢出。

内存分配完毕后,将调用memcpy函数,从而将图像数据复制到分配的内存中。

图像的复制是逐行进行的。

```

memcpy(allocated_address + bytes_copied ,line_buffer, width*output_component);//<--Oops

```

也就是说,每次复制的数据量为width*output_component,复制的次数为height。

从利用漏洞的角度来看,这是一个很有“前途”的漏洞:线性堆溢出使攻击者能够控制分配的内存块的大小、溢出的数量以及溢出内存区域的内容。

小结

在本文中,我们为读者详细介绍了Instagram应用代码执行漏洞有关的背景知识,以及通过模糊测试在Mozjpeg项目中发现的安全漏洞,在下一篇文章中,我们将为读者介绍wildcopy型漏洞的利用方法等内容。

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

相关推荐: 渗透测试中常用的文件下载方式总结

0x01 Windows certutil certutil是Windows自带的工具,具有下载文件,校验文件MD5、SHA1、SHA256,文件base64编码等功能。 使用certutil下载文件,保存在当前路径,文件名称与下载文件名称相同: certut…

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年4月15日01:05:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Instagram应用代码执行漏洞详解(一)http://cn-sec.com/archives/246733.html