spaceline是军用软件吗

Instagram每天上传超过100+百万张照片,是最受欢迎的社交媒体平台之一。 基于这个原因,我们决定测试适用于Android和iOS操作系统的Instagram应用的安全性。 在其中我们发现了一个严重漏洞,可以用来在受害者的手机上远程执行任意代码。

我们进行此研究的关注方向是测试Instagram使用的第三方项目组件。

现如今,许多软件开发人员,无论其项目规模大小,都会在其软件中使用开源项目。在此次研究的最后,我们在Instagram使用的Mozjpeg(一个开源项目用作其JPEG格式解码器)库中发现了一个漏洞。

在我们下面描述的攻击情形中,攻击者只需要通过电子邮件,或其他媒体交换平台将图像发送给受害者。 当受害者打开Instagram应用程序时,就会执行恶意代码。

告诉我你的朋友是谁,我会告诉你他的漏洞

我们都知道,即使是大公司大都也依赖于开源项目,并且这些项目几乎不经过修改就集成到了他们的产品中。

大多数使用第三方开源项目的公司都对其进行了声明,但并非所有库都显示在该应用程序的“About”页中。 为了确保能查看到所有的库最好的办法是查看Instagram应用的lib-superpack-zstd目录:

moz后缀是mozjpeg的缩写,mozjpeg是Mozilla JPEG编码器项目的缩写,我们需要了解一些这些模块都做了什么?

让我们从JPEG格式的简要历史开始。JPEG是一种自1990年代初期以来就存在的图像文件格式,它基于有损压缩的概念,这意味着在压缩过程中会丢失一些信息,但是人眼可以忽略不计。Libjpeg是Windows,Mac和Linux操作系统中内置的基准JPEG编码器,由一个非正式的独立小组维护。该库试图在编码速度和质量与文件大小之间取得平衡。

相反,是libjpeg的高性能替代品,并且是大多数Linux发行版的默认库。该库旨在在编码和解码期间使用较少的CPU时间。

2014年3月5日,Mozilla发布了项目,这是一个基于libjpeg-turbo之上的JPEG编码器,目的是为Web图像提供更好的压缩效果,但会降低性能。

在Instagram的使用中将mozjpeg库拆分为3个不同的共享对象:

  • libjpegutils_moz.so –两个共享对象之间的连接器。它包含JNI调用以从Java应用程序端调用解压缩的API。

我们在CPR的团队建立了一个多处理器模糊测试实验室,通过我们以往的为我们带来了惊人的结果,因此我们决定将模糊测试工作也扩展到Mozjpeg。

Mozilla在libjpeg-turbo之上所做的主要添加是压缩算法,因此这才是我们研究的重点。

AFL是我们首选的武器,因此自然而然地我们必须为其编写harness。

要编写harness,我们必须了解如何使用Mozjpeg解压功能。

幸运的是,Mozjpeg附带了一个代码示例,说明了如何使用该库:

但是,为确保我们在Mozjpeg中发现的任何崩溃都影响Instagram本身,我们需要了解Instagram是如何将Mozjpeg集成到其代码中的。

幸运的是,下面你可以看到Instagram直接复制粘贴了该库的最佳实践:

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

我们仅用30个核心的CPU跑了fuzzer一天,就收到了AFL通知我们的447次独特的崩溃。

在对结果进行分类之后,我们发现了与解析JPEG图像Size有关的有趣崩溃。 崩溃是一个越界写,我们决定专注研究它。

存在漏洞的函数是read_jpg_copy_loop,它在解压缩过程中产生了整数溢出。

漏洞的函数在解析JPEG图像文件时处理图像尺寸不恰当。下面是原始漏洞函数的伪代码:

首先,让我们了解这段代码的作用。

_wrap_malloc函数基于3个参数(即图像尺寸)来分配内存块。宽度和高度都是从文件中解析的16位整数(uint16_t)。

除了高度和宽度,output_component也完全由攻击者控制。它是从文件中解析的,并且未针对文件中可用的剩余数据进行验证。

__warp_malloc期望其参数在32位寄存器中传递!这意味着,如果我们可以使分配大小超过(2 ^ 32)个字节,则将发生整数溢出,从而导致分配的大小比预期的小得多。

分配的尺寸是通过将图片的宽度,高度和output_components相乘得出的。这些大小不受检查,由我们控制。滥用它们便会得到我们想要的整数溢出。

更为方便我们的是,此缓冲区然后会传递给memcpy,从而导致基于堆的缓冲区溢出。

在分配内存后,将调用memcpy函数,并将图像数据复制到分配的内存中。

从漏洞利用者的角度来看,这是一个很有希望的bug:线性堆溢出使攻击者可以控制分配的大小,溢出的大小以及溢出的内存区域的内容。

为了导致内存损坏,需要溢出整数以确定分配大小; 因此计算的大小必须超过32位所能表示的最大值。 此时我们就是正在处理一个WildCopy的漏洞利用,这意味着我们必须复制大于2^32(4GB)的数据。 因此,当循环到达未映射的页面时,程序极有可能崩溃:

那么我们如何利用这一点呢?

在深入研究Wildcopy利用技术之前,我们需要将我们的情况与经典的wildcopy(如)区分开来。 经典案例通常涉及一个写入4GB数据的memcpy

但是,在我们的示例中,有一个for循环尝试将X字节复制Y次,而X * Y为4GB。

当我们尝试利用这种内存损坏漏洞时,我们需要问自己几个重要的问题:

  • 我们能否控制(甚至部分控制)我们破坏的数据的内容?
  • 我们可以控制要破坏的数据的长度吗?
  • 我们可以控制溢出的已分配块的大小吗?

最后一个问题尤其重要,因为在Jemalloc/LFH(或每个基于bucket的分配器)中,如果我们无法控制要破坏的块的大小,则可能难以对堆进行布局以进一步破坏一个特定目标结构(如果该结构的大小明显不同)。

一眼望去,关于我们控制内容的能力的第一个问题的答案似乎是“是”,因为我们可以控制图像数据的内容。

现在,转到第二个问题–控制我们破坏数据的长度。 答案也显然是“是”,因为memcpy循环逐行复制文件,并且复制的每一行的大小都是由攻击者控制的width参数和output_component的乘积。

关于我们破坏的缓冲区大小的第三个问题的答案是微不足道的。

由于它是由width * height * cinfo-> output_components控制的,因此我们编写了一个小Python脚本,该脚本根据整数溢出的影响,根据希望分配的块大小,为我们提供了这三个参数的含义:

现在,我们已经具备了使用wildcopy的先决条件,让我们看看如何利用它们。

要触发此漏洞,我们必须指定一个大于2 ^ 32字节的数据长度。实际上,我们需要在到达未映射的内存之前停止wildcopy

  • 依靠竞态条件–尽管wildcopy破坏了一些有用的目标结构或内存,但是我们可以竞争另一个线程,以在wildcopy崩溃之前使用现在已损坏的数据来执行某些操作(例如,构造其他原语,终止wildcopy等)。
  • 如果wildcopy循环具有某种逻辑,可以在某些情况下停止循环,那么我们可以打乱这些检查,并在破坏足够的数据后停止循环。
  • 如果wildcopy循环在每次迭代时都调用一个虚函数,并且指向该函数的指针位于堆内存中的结构中(或位于我们在wildcopy期间可能损坏的另一个内存地址中),则漏洞利用程序可以使用该循环来覆盖并在wildcopy期间转移执行。

遗憾的是,第一种方法不适用于此处,因为我们是从图像向量中进行攻击。 因此,我们对线程没有任何控制,因此竞态条件选项无济于事。

为了使用第二种方法,我们寻找了一个终止开关来停止wildcopy。 我们尝试将文件切成两半,同时保持图像标题中的大小相同。 但是,我们发现,如果解码库达到一个EOF标记,它只会添加另一个EOF标记,因此我们最终陷入EOF标记的无限循环中。

我们还尝试寻找一种ERREXIT函数,该函数可以在运行时停止解压缩过程,但是我们了解到,无论我们做什么,我们都永远无法在此代码中找到导致ERREXIT的路径。 因此,第二种选择也不适用。

要使用第三个选项,我们需要寻找一个在wildcopy循环的每次迭代中都会调用的虚函数。

让我们回到发生memcpy复制的循环逻辑:

我们可以看到,除了覆盖我们的memcpy… 之外,只有一个函数可以在每次迭代中调用—jpeg_read_scanlines

在上面的代码中我们可以看到,每次调用jpeg_read_scanlines从文件中读取另一行时,我们都会调用虚拟函数process_data

从文件中读取的行被复制到cinfo结构内的row_ctr的缓冲区中。

process_data_simple_main中,我们可以确定另外2个在每次迭代中都会调用的虚函数。 它们都有一个cinfo结构。

Cinfo是在Mozjpeg各种函数期间传递的结构。 它包含关键成员,函数指针和图像元数据。

cinfo结构中,我们可以看到3个指向函数的指针,这些指针可以在循环覆盖期间尝试覆盖并劫持执行流。

事实证明,上面提到的第三个方案适用于我们的情况!

在深入探讨Jemalloc利用概念之前,我们需要了解Android的堆分配器的工作方式,以及我们将在下一章着重介绍的所有术语-Chunks, Runs, Regions。

Jemalloc是一个基于bucket的分配器,它将内存分成大小始终相同的块(Chunks),并使用这些块(Chunks)存储所有数据结构(以及用户请求的内存)。 块被进一步划分为“runs”,它负责特定大小的请求/分配。 run会跟踪这些大小块的free操作和使用过的“Regions(区域)”。 Regions是在用户空间分配(malloc调用)上返回的堆块。 最后,每次运行都与一个“bin”相关联。Bins负责存储free区域(Regions)的结构(树)。

我们发现了3个好的函数指针,可用于在wildcopy期间转移执行并控制PC寄存器。

cinfo结构具有以下成员:

我们需要找到3种结构在堆存储器中的位置,基于此我们可以覆盖其中至少一种结构以获得对PC寄存器的控制。

为了弄清楚这一点,我们需要了解使用Mozjpeg解压缩图像时堆的样子。

让我们回想一下cinfo最重要的struct成员之一:

Mozjpeg有自己的内存管理器。JPEG库的内存管理器控制内存的分配和释放,并管理大型“虚拟”数据数组。 库中的所有内存和临时文件分配都是通过内存管理器完成的。 这种方法有助于防止内存泄漏问题,并且在malloc/free运行缓慢时加速操作。

内存管理器创建被释放内存的“内存池”,并且可以一次释放整个池。

一些数据是被“永久”分配使用的,直到销毁JPEG对象后才会释放。

例如,让我们看一下Mozjpeg在图像解码过程中所做的一种堆的分配。 当Mozjpeg要求分配0x108字节时,堆分配器实际上会给一个大小为0x777的堆块。 我们看到,请求的大小和分配的实际大小不同。

我们需要来分析一下这种行为。

分配的“池”由alloc_small和其他封装的函数管理,这些函数维护一组成员,以帮助它们监视“池”的状态。 因此,每当请求内存分配时,封装的函数都会检查“池”中是否有足够的空间。

如果有可用空间,则alloc_small函数从当前“池”中返回一个地址,并使指向池中空闲块的指针移动。

当“池”空间不足时,它将使用从first_pool_slop数组中读取的预定义的内存块分配另一个“池”,在我们的示例中为160016000

现在,我们了解了Mozjpeg的内存管理器是如何工作的,我们需要确定哪个内存“池”保存着目标虚拟函数指针。

作为解压缩过程的一部分,有两个主要函数可对图像元数据进行解码并为以后的处理做好准备。直到我们到达wildcopy循环为止,仅有jpeg_read_headerjpeg_start_decompress两个函数负责内存分配。

在解析这些标记时,第二个也是最大的“池”,大小为e80),由Mozjpeg内存管理器分配。“池”的大小是first_pool_slop数组(来自上面的代码段)中的const值,这意味着Mozjpeg的内部分配器已经使用了第一个池的所有空间。

我们知道,我们的目标main,coef和post结构是从jpeg_start_decompress函数中分配的。因此,我们可以放心地假设其他分配的结构(直到我们到达wildcopy循环)也将位于第二个大“池”中,包括我们要覆盖的main,coef和post结构!

现在,让我们仔细看看Jemalloc如何处理这种大小类分配。

Jemalloc返回的内存大小分为三类-小,大,巨大。

  • 小型/中型:这些区域小于内存页大小(通常为4KB)。
  • 大:这些区域介于小/中和大之间(内存页大小与块(chunk)大小之间)。
  • 巨大:大于块(chunk)的大小。它们被单独处理与arenas无关,有一个全局分配器树。

操作系统返回的内存分为多个部分。在Android中,对应于不同的版本,这些块的大小也不相同。它们通常约为2MB/4MB。每个块都与一个arena关联。

run可用于托管一个分配的大块或分配的多个小块。

大型regions有自己的runs,即每个分配的大块都有专用的run。

在为漏洞利用实现堆布局时,有一种可视化堆的方法:查看堆上下文中的各种内存分配。

为此,我们使用一个简单的工具,使我们可以在漏洞利用开发过程中检查目标进程的堆状态。 我们使用了argpvats编写的名为的工具来可视化Jemalloc堆。

我们使用gdb上的shadow执行了调试会话,以验证我们的假设。

我们的目标是利用整数溢出来导致堆缓冲区溢出。

利用这些类型的bug都是对于堆对象的精确定位。我们想强制某些对象被分配在堆中的特定位置,因此我们可以布局形成有用的邻接关系来破坏内存。

为了实现这种邻接,我们需要调整堆的布局,以便将可利用的内存对象分配在目标对象之前。

不幸的是,我们无法控制free操作。根据Mozjpeg文档,大多数数据是按“per

但是,在我们的例子中,我们不需要任何free操作,因为我们可以控制一个函数,该函数最终会执行内存分配大小受我们控制的malloc函数。这使我们能够选择将可溢出的缓冲区放在堆上的位置。

我们想将可以溢出的缓冲区对象放置在执行函数指针调用的main/post/coef大型(0x5000)数据结构对象之前。

因此,我们利用此漏洞的最简单方法是对堆进行布局,以便将溢出的缓冲区分配到我们的目标(0x5000byte)对象之前,然后(使用该错误)覆盖main/post/coef虚函数地址。这使我们可以完全控制虚表,将任意方法重定向到指定的地址。

我们知道目标对象具有相同的大小(0x5000),并且因为Jemalloc从顶到底分配空间,所以我们唯一需要做的就是将溢出对象放在目标块所在的底部。

对象之间的距离(以字节为单位)并不重要,因为我们有一个wildcopy循环,可以逐行复制大量数据(我们控制行的大小)。被复制的数据最终大于2MB,因此我们可以确定,最终我们将破坏溢出对象之后的块中的每个对象。

由于我们无法控制free操作,因此无法创建对象会掉进去的洞。 (洞是run过程中一个或多个被free的位置。)相反,我们尝试查找在图像解压缩流程中无论如何都会出现的洞,并寻找在调试期间反复出现的大小。

让我们使用shadow工具检查块在内存中的布局:

我们正在寻找带有洞的runs,并且这些runs必须在要覆盖的较大目标缓冲区之前。 run可用于托管一个大块的或多个小型/中型的分配操作。

承载小分配的runs被分为区域(regions)。一个区域(regions)等同于一个小的分配。 每个小的runs仅容纳一个区域(regions)。 换句话说,一个小runs恰好与一个区域(regions)大小相关联。

托管中型分配的runs也被划分为多个区域,但顾名思义,它们大于小型分配。 因此,托管介质分配的runs被划分为占用更多空间的类区域。

大小为0x200的中型run被分为8个区域:

小块分配是最常见的,很可能是我们需要manipulate/control/overflow的分配。 由于将较小的分配划分为更多的区域(regions),因此它们更易于控制,因为由其他线程来分配其余区域(regions)的可能性较小。

因此,为了使可溢出对象分配到大型目标对象之前,我们使用我们的Python脚本(wild copy利用)。 该脚本可帮助我们生成需要的图片尺寸,这将使得malloc在目标的小型类中分配可溢出对象。

我们构造了一个新的JPEG图像,其大小(0xe0)将会触发分配给对象的小型类,并在libjepgutils_moz.so + 0x918上设置了一个断点。

我们在可控的malloc之前只有一条命令,X0寄存器刚好拥有我们希望分配的大小:

我们继续执行一个命令,然后再次检查X0寄存器,该寄存器现在保存了调用malloc后返回的结果:

我们从malloc获取的地址是我们可溢出对象的地址(0x72a639ac40)。 让我们使用shadow框架中的jeinfo方法检查其在堆上的位置。

让我们继续执行,看看覆盖大型目标对象后会发生什么

BOOM! 正是我们的目标–当程序试图通过函数指针从溢出的对象中加载被传入数据破坏的函数地址时,崩溃发生了。 我们遇到了总线错误(也称为SIGBUS,通常为信号10),该错误在进程尝试访问CPU无法物理寻址的内存时发生。 换句话说,程序尝试访问的内存不是有效的内存地址,因为它包含来自我们映像的数据,该数据替换了实函数指针并导致崩溃!

我们有一个可控的函数调用。 编写一个可靠的漏洞利用所缺少的就是将可执行内存重定向到一个方便的堆栈块构建ROP。

现在我们需要将所有内容放在一起:

  1. 生成我们控制的有效payload的副本
  2. 将执行跳转到受我们控制的地址。

我们需要使用受控数据来生成损坏的JPEG。 因此,我们的下一步是确切确定Mozjpeg平台支持哪些图像格式。 我们可以从下面的代码中找出答案。 out_color_space表示根据图像格式确定的每个像素的位数。

我们使用了一个称为的简单Python库来构造RGB BMP文件。 我们选择了我们熟悉的RGB格式,并在其中填充了“ AAA”作为有效载荷。 该文件是我们用来创建恶意压缩JPEG的基本图像格式。

然后,我们使用Mozjpeg项目中的cjpeg工具将bmp文件压缩为JPEG文件。

接下来,我们测试了压缩后的输出文件以验证我们的假设。 我们知道RGB格式是每个像素3个字节。

但是,当我们检查控制的分配时,我们看到作为整数溢出一部分的heightwidth参数仍然乘以值为4的out_color_components,纵然我们从RGB格式开始,每行使用3x8-bit像素。 看来Mozjpeg更倾向于将我们的图像转换为每个像素4x8-bit的格式。

然后,我们转向了Mozjpeg平台支持的4x8-bit像素格式,CMYK格式满足了标准。我们使用CMYK格式作为基本图像,以便完全控制所有4个字节。我们在图像中填充了“AAAA”作为有效载荷。

我们将其压缩为JPEG格式,并添加了可以触发程序崩溃的图片尺寸。 令我们振奋的是,我们发生了以下崩溃!

但是,即使我们在每个像素图像上构建了4x8-bit的图像,我们也还是得到了一个奇怪的0xFF字节在我们可控地址中,然而其却并非有效载荷的一部分。

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

支持透明的位图文件格式包括GIF,PNG,BMP,TIFF和JPEG 2000(通过颜色透明或Alpha通道)。

基于位图的图像在技术上是通过图像的宽度和高度(以像素为单位)以及每个像素的位数来表征的。

因此,我们决定使用PIL库通过受控的alpha通道(0x61)构造RGBA BMP格式文件。

令人惊讶的是,我们得到的结果与使用CMYK构造的恶意JPEG触发崩溃的结果相同。 即便我们使用了RGBA格式作为压缩JPEG的基础,我们仍然在可控的地址中看到了0xFF,并且文件中的Alpha通道的值为(0x61)。 这怎么发生的? 让我们回到代码中,了解造成这种奇怪行为的原因。

最终我们在下面的这段小代码中找到了答案:

这也解释了为什么即使我们使用每个像素3x8-bit的RGB对象,也得到了奇怪的0xFF alpha通道。

现在我们将所有内容放在一起,得出的结论是,无论将哪种图像格式用作压缩JPEG的基础,Instagram始终会将输出文件转换为RGBA格式文件。

始终在开头添加0xff这一事实意味着我们可以在大端环境中实现我们的目标。

小端系统将单词的最低有效字节存储在最小的存储地址中。 因为我们使用的是低位优先系统,所以Alpha通道值始终被改写为受控地址的最高有效字节。 当我们尝试在用户模式下利用该错误,并且(0xFF)开头的地址值属于内核地址空间时,它会直接挫败我们的计划。

我们失去了快速取得胜利的可能。 但可以从中学到的一课是,现实生活不是CTF游戏,有时开发人员设定的一个关键const值可能会从开发角度破坏一切。

让我们回想一下Mozilla基金会主要网站上有关Mozjpeg的内容:

Mozjpeg的唯一目的是减少网络上提供的JPEG文件的大小。

据我们所知,每上传一张图片,Instagram就会增加25%的内存使用量!大约每天1亿美元!

至此,Facebook已经修补了该漏洞,因此即使我们还没有完全解决它,我们也停止了利用开发工作。

我们仍然只有3个字节的覆盖,从理论上讲,我们可以投入更多的时间来找到更多有用的原语,以帮助我们利用此错误。但是,我们认为我们已经做了足够的工作,并且已经宣传了我们想要传达的重要观点。

Spaces 的出现,是为了解决群组共享里的困扰:

- 经常需要在不同 APP 间拷贝粘贴链接;

- 在你需要的时候,群聊里有价值的信息最终往往找不到。

在 Spaces 里,可以就在 APP 里简单快速地分享文章、视频、图片。带着 Google 的加成,Spaces 里的搜索也很牛。

几天的简单体验,我觉得 Google Spaces 的亮点在于:

- 随处可见:在 Google 系列的 Web APP 顶栏右侧,随时会冒出 Spaces 的通知消息来,这比较容易将用户召唤进 Spaces 里;

- 在 APP 里分享:发任何东西都是基于内置的 Chrome 快速搜索、分享。不需要在多个应用里切换;

- 内容随时可以找到:无可非议地,在 Spaces 里说的内容、发的图片或视频,在这里都能最精准地被搜索。

- 添加链接时的交互很自然,无需跳出 APP

- 圈子内容为空时显示「让 Google 协助你创建第一条信息」

- 圈子成员为空时显示「通过以下方式邀请……」,点击后,iOS 直接拉起 Apple 的共享窗口

首先,在中国肯定没有任何机会。我估计普通小团队(尤其可能是松散团队)很难人人会翻墙,并且愿意忍受翻墙的麻烦来用 Google Spaces。

那在国外有机会吗?说实话,我觉得悬,基于以下几个原因:

- 在社交产品上,Google 的纪录一向不太好,做一个关一个已经习惯了,用户的信任感都快丢光了;

- 发布 Spaces 没几天后的 Google IO 上,Google 还推出了 Allo & Duo,多手互博,看得我眼花缭乱,不知道他们到底是在炫技,还是在做产品;

当然有,可以看看小密圈()。小密圈是小团队共享工具,以「分组朋友圈」的形式,让团队、社群可以安全分享文件和信息。用小密圈可以做公司与团队内部知识管理、产品对外的交流反馈社区、组建兴趣与同好社群等。

小密圈主要服务于企业和大 V,尽量优化他们的使用体验。在深度使用小密圈后,你或许会发现小密圈有一些隐藏很深但是很有意思的功能点。比如对链接的处理:

- 在其他 APP 里复制了一个链接后,打开小密圈,会自动提示你是否粘贴;

- 贴进链接后,开始是个 URL,后台会自动识别页面主题,并把 URL 转换为主题文字;

- 如果这个链接被删除了,点击 URL 前面的「链接」二字,可以看到小密圈还保存了链接的 HTML 缓存和图片缓存,再也不怕链接被和谐了。

比如对转发到微信的处理:

- 圈主可以设置是否允许转发到微信和朋友圈;

- 被转发到微信后,通过微信阅读过的人,都会被记录下来。

- 允许上传 Office、WPS、PDF 等文档(朋友圈里可以没有这个待遇);

- 在圈子里对文档做内容识别与聚类,圈内搜索的时候,也对文件内容进行全文搜索。

我要回帖

更多关于 spacetime翻译 的文章

 

随机推荐