Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
extra_recipe — Ian Beer 的 CVE-2017-2370 漏洞利用程序(在 iOS 10.2 上实现内核内存读写) | Kitploit
工具/GitHubGitHub/ldebug/extra_recipe
iOS安全内存取证漏洞分析漏洞利用移动安全二进制利用
GitHubldebug/extra_recipe

extra_recipe

Ian Beer 的 CVE-2017-2370 漏洞利用程序(在 iOS 10.2 上实现内核内存读写)

查看仓库
49年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
网站

// ianbeer

针对 iOS 10.2 上 CVE-2017-2370 的内核任意读写利用程序

仅在 iPod Touch 6G 14C92 上测试过——其他设备/固件无法开箱即用!

*** 漏洞 ***

mach_voucher_extract_attr_recipe_trap 是一个可以从任何上下文调用的 mach trap。它是 iOS 10 中新增的代码。

kern_return_t mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args *args) { ipc_voucher_t voucher = IV_NULL; kern_return_t kr = KERN_SUCCESS; mach_msg_type_number_t sz = 0;

root@kitploit:~
if (copyin(args->recipe_size, (void *)&sz, sizeof(sz)))              <---------- (a)
  return KERN_MEMORY_ERROR;

if (sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE)
  return MIG_ARRAY_TOO_LARGE;

voucher = convert_port_name_to_voucher(args->voucher_name);
if (voucher == IV_NULL)
  return MACH_SEND_INVALID_DEST;

mach_msg_type_number_t __assert_only max_sz = sz;

if (sz < MACH_VOUCHER_TRAP_STACK_LIMIT) {
  /* keep small recipes on the stack for speed */
  uint8_t krecipe[sz];
  if (copyin(args->recipe, (void *)krecipe, sz)) {
    kr = KERN_MEMORY_ERROR;
    goto done;
  }
  kr = mach_voucher_extract_attr_recipe(voucher, args->key,
      (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
  assert(sz <= max_sz);

  if (kr == KERN_SUCCESS && sz > 0)
    kr = copyout(krecipe, (void *)args->recipe, sz);
} else {
  uint8_t *krecipe = kalloc((vm_size_t)sz);                          <---------- (b)
  if (!krecipe) {
    kr = KERN_RESOURCE_SHORTAGE;
    goto done;
  }

  if (copyin(args->recipe, (void *)krecipe, args->recipe_size)) {    <----------- (c)
    kfree(krecipe, (vm_size_t)sz);
    kr = KERN_MEMORY_ERROR;
    goto done;
  }

  kr = mach_voucher_extract_attr_recipe(voucher, args->key,
        (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
  assert(sz <= max_sz);

  if (kr == KERN_SUCCESS && sz > 0)
    kr = copyout(krecipe, (void *)args->recipe, sz);
  kfree(krecipe, (vm_size_t)sz);
}

kr = copyout(&sz, args->recipe_size, sizeof(sz));

done:
ipc_voucher_release(voucher);
return kr;

}

以下是参数结构(由用户态控制)

struct mach_voucher_extract_attr_recipe_args { PAD_ARG_(mach_port_name_t, voucher_name); PAD_ARG_(mach_voucher_attr_key_t, key); PAD_ARG_(mach_voucher_attr_raw_recipe_t, recipe); PAD_ARG_(user_addr_t, recipe_size); };

recipe 和 recipe_size 是用户态指针。

在 (a) 处,从用户态指针 recipe_size 读取四个字节到 sz。

在 (b) 处,如果 sz 小于 MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE (5120) 且大于 MACH_VOUCHER_TRAP_STACK_LIMIT (256), 则使用 sz 分配一个内核堆缓冲区。

在 (c) 处,再次调用 copyin 将用户态内存复制到刚刚分配的缓冲区中,但传入的大小不是 sz(已校验并据此分配的大小), 而是 args->recipe_size。这是用户态指针指向大小,而不是大小本身!

这导致一个完全受控制的内核堆溢出。注意,这段代码实际上根本无法正常工作 :)

*** 利用 ***

我瞄准的是通过 kalloc 分配的预分配 mach 消息缓冲区。前 4 个字节是大小字段,用于确定 在缓冲区中的什么位置读写消息。通过破坏这个字段,我们可以让 mach 消息在支撑 kmsg 的 kalloc 分配范围之外进行读写。

有一个小麻烦:端口预分配的 kmsg 只会被内核用于实际的 mach_msg 发送(例如不会用于 MIG 方法的回复)。 这使得在其中放入足够多的受控内容变得有点棘手。

内核发送的一种携带大量用户可控数据的 mach 消息是异常消息(exception message),在线程崩溃时发送。

文件 load_regs_and_crash.s 包含 ARM64 汇编,它将 ARM64 通用寄存器加载为某个缓冲区的内容, 这样当线程崩溃时,异常消息会包含该数据缓冲区(大约有 0x70 字节是可控的)。

通过覆盖端口的 ikm_size 字段,使其指向另一个端口的头部,我们可以读写另一个端口的头部,并得知它在内存中的位置。 然后我们可以释放第二个端口,并在其位置重新分配一个 user client,同样可以对该 user client 进行读写。

我读取了 userclient 的 vtable 指针,然后使用 OSSerializer::serialize 小工具技术(详见 [https://info.lookout.com/rs/051-ESQ-475/images/pegasus-exploits-technical-details.pdf]) 以两个受控参数调用任意函数。

我调用 uuid_copy,它会执行 memmove(arg0, arg1, 0x10)。通过将 arg0 或 arg1 指向 userclient 本身(我们可以通过接收 异常消息来读取它),就可以以 16 字节为单位读写任意内核内存。

下载工具