0x00 概述
在macOS 10.15.2版本上,Apple引入了com.apple.private.security.clear-library-validation权限(entitlement),该权限正在逐渐取代以前在系统二进制文件上使用的com.apple.security.cs.disable-library-validation权限。尽管二者的影响大致相同,但它们的工作原理却存在差异。尽管使用com.apple.security.cs.disable-library-validation和com.apple.private.security.clear-library-validation会自动禁用库验证,但应用程序必须通过csops系统调用将其禁用。
0x01 简介
在Big Sur版本发布后,我注意到许多系统二进制文件都具有新的权限,其中的com.apple.private.security.clear-library-validation是我此前没有接触过的。这些应用程序之前使用的是com.apple.security.cs.disable-library-validation,看来它们似乎已经被一个新的权限替换。由于二者的名字比较相似,并且经过测试也证实了这些二进制文件仍然可以加载非Apple开发人员签名的第三方插件。这意味着,这些权限具有相同的影响。但是,二者的内部工作原理是不同的。
0x02 csops系统调用
在遇到下面列出的新csops操作代码后,我开始深入研究这一新的权限,可以在xnu-7195.50.7.100.1/bsd/sys/codesign.h中找到该代码。
#define CS_OPS_CLEAR_LV 15 /* clear the library validation flag */
csops是一个系统调用,可以用于对进程执行各类与代码签名相关的操作。我们可以查询进程的状态,在运行时设置各种标志,查询其代码签名blob等等。这是我以前没有发现过的新功能,因此我开始对其进行分析。
根据这个常量的说明,我们可以使用这个操作代码来清除进程的库验证标志。这意味着,如果我们可以在某个进程上运行它,则在调用成功的情况下,可以将第三方库加载到该进程中。
这个常量仅在xnu-7195.50.7.100.1/bsd/kern/kern_proc.c源文件中引用,该文件中包含csops_internal函数的源代码。这是在进行系统调用时将会运行的函数。下面是与CS_OPS_CLEAR_LV操作相关的部分源代码。
static int csops_internal(pid_t pid, int ops, user_addr_t uaddr, user_size_t usersize, user_addr_t uaudittoken) { (...) if (pid == 0) { pid = proc_selfpid(); } if (pid == proc_selfpid()) { forself = 1; } switch (ops) { case CS_OPS_STATUS: case CS_OPS_CDHASH: case CS_OPS_PIDOFFSET: case CS_OPS_ENTITLEMENTS_BLOB: case CS_OPS_IDENTITY: case CS_OPS_BLOB: case CS_OPS_TEAMID: case CS_OPS_CLEAR_LV: break; /* not restricted to root */ default: if (forself == 0 && kauth_cred_issuser(kauth_cred_get()) != TRUE) { return EPERM; } break; } pt = proc_find(pid); if (pt == PROC_NULL) { return ESRCH; } (...) #if CONFIG_MACF switch (ops) { case CS_OPS_MARKINVALID: case CS_OPS_MARKHARD: case CS_OPS_MARKKILL: case CS_OPS_MARKRESTRICT: case CS_OPS_SET_STATUS: case CS_OPS_CLEARINSTALLER: case CS_OPS_CLEARPLATFORM: case CS_OPS_CLEAR_LV: if ((error = mac_proc_check_set_cs_info(current_proc(), pt, ops))) { goto out; } break; default: if ((error = mac_proc_check_get_cs_info(current_proc(), pt, ops))) { goto out; } } #endif switch (ops) { (...) case CS_OPS_CLEAR_LV: { /* * This option is used to remove library validation from * a running process. This is used in plugin architectures * when a program needs to load untrusted libraries. This * allows the process to maintain library validation as * long as possible, then drop it only when required. * Once a process has loaded the untrusted library, * relying on library validation in the future will * not be effective. An alternative is to re-exec * your application without library validation, or * fork an untrusted child. */ #if !defined(XNU_TARGET_OS_OSX) // We only support dropping library validation on macOS error = ENOTSUP; #else /* * if we have the flag set, and the caller wants * to remove it, and they're entitled to, then * we remove it from the csflags * * NOTE: We are fine to poke into the task because * we get a ref to pt when we do the proc_find * at the beginning of this function. * * We also only allow altering ourselves. */ if (forself == 1 && IOTaskHasEntitlement(pt->task, CLEAR_LV_ENTITLEMENT)) { proc_lock(pt); pt->p_csflags &= (~(CS_REQUIRE_LV | CS_FORCED_LV)); proc_unlock(pt); error = 0; } else { error = EPERM; } (...) }
我们将一步一步进行介绍。其中,有三个地方会对其进行检查。我们首先来看第一个swtich条件语句。
switch (ops) { case CS_OPS_STATUS: case CS_OPS_CDHASH: case CS_OPS_PIDOFFSET: case CS_OPS_ENTITLEMENTS_BLOB: case CS_OPS_IDENTITY: case CS_OPS_BLOB: case CS_OPS_TEAMID: case CS_OPS_CLEAR_LV: break; /* not restricted to root */ default: if (forself == 0 && kauth_cred_issuser(kauth_cred_get()) != TRUE) { return EPERM; } break; }
在这里,系统将允许非root执行switch条件中列出的操作,其中的一项是我们关注的重点。这表明,即使我们没有以root用户身份运行,也可以使用CS_OPS_CLEAR_LV操作调用csops。
接下来,我们来分析另一个switch条件。
#if CONFIG_MACF switch (ops) { case CS_OPS_MARKINVALID: case CS_OPS_MARKHARD: case CS_OPS_MARKKILL: case CS_OPS_MARKRESTRICT: case CS_OPS_SET_STATUS: case CS_OPS_CLEARINSTALLER: case CS_OPS_CLEARPLATFORM: case CS_OPS_CLEAR_LV: if ((error = mac_proc_check_set_cs_info(current_proc(), pt, ops))) { goto out; } break; default: if ((error = mac_proc_check_get_cs_info(current_proc(), pt, ops))) { goto out; } } #endif
在这里,我们使用mac_proc_check_get_cs_info函数进行了MACF策略调用。如果成功,MACF策略调用将返回0,这就是对条件的检查。在xnu-7195.50.7.100.1/security/mac_process.c内部实现了mac_proc_check_get_cs_info函数。我们跟踪这个函数。
int mac_proc_check_set_cs_info(proc_t curp, proc_t target, unsigned int op) { kauth_cred_t cred; int error = 0; #if SECURITY_MAC_CHECK_ENFORCE /* 21167099 - only check if we allow write */ if (!mac_proc_enforce) { return 0; } #endif if (!mac_proc_check_enforce(curp)) { return 0; } cred = kauth_cred_proc_ref(curp); MAC_CHECK(proc_check_set_cs_info, cred, target, op); kauth_cred_unref(&cred); return error; }
该函数最终将使用MAC_CHECK宏进行MACF调用,我在针对CVE-2020-9771补丁的逆向分析过程中已经讨论过。它将遍历MACF策略挂钩,该挂钩对proc_check_set_cs_info进行检查。目前,它仅仅被沙箱挂钩,如下所示。
void _hook_proc_check_set_cs_info(int arg0, int arg1) { ___bzero(&var_1A0, 0x188); *(int32_t *)(&var_1A0 + 0xa8) = 0x4; *(&var_1A0 + 0xb8) = arg1; _cred_sb_evaluate(arg0, 0x65, &var_1A0, rcx, r8, r9); return; }
在这里,将使用操作码0x65对_cred_sb_evaluate进行内部调用,我在上一篇文章中也对此进行了讨论。
回到我们的csops调用中,不论调用过程允许或不允许这个操作,都会运行MACF策略检查。
假设允许这个操作,那么我们继续,最后到达实际执行该操作的位置。
case CS_OPS_CLEAR_LV: { /* * This option is used to remove library validation from * a running process. This is used in plugin architectures * when a program needs to load untrusted libraries. This * allows the process to maintain library validation as * long as possible, then drop it only when required. * Once a process has loaded the untrusted library, * relying on library validation in the future will * not be effective. An alternative is to re-exec * your application without library validation, or * fork an untrusted child. */ #if !defined(XNU_TARGET_OS_OSX) // We only support dropping library validation on macOS error = ENOTSUP; #else /* * if we have the flag set, and the caller wants * to remove it, and they're entitled to, then * we remove it from the csflags * * NOTE: We are fine to poke into the task because * we get a ref to pt when we do the proc_find * at the beginning of this function. * * We also only allow altering ourselves. */ if (forself == 1 && IOTaskHasEntitlement(pt->task, CLEAR_LV_ENTITLEMENT)) { proc_lock(pt); pt->p_csflags &= (~(CS_REQUIRE_LV | CS_FORCED_LV)); proc_unlock(pt); error = 0; } else { error = EPERM; }
在这里,Apple添加了非常详细的注释,对所有内容都进行了说明。如果满足要求,它将清除目标进程的库验证标志(pt->p_csflags &= (~(CS_REQUIRE_LV | CS_FORCED_LV));)。
条件非常严格。该操作只能由进程自身(forself == 1)以及具有由常量CLEAR_LV_ENTITLEMENT定义的权限的进程调用。这是在xnu-7195.50.7.100.1/bsd/sys/codesign.h中进行的定义。
#define CLEAR_LV_ENTITLEMENT "com.apple.private.security.clear-library-validation"
在循环结束后,我们就得到了以前曾见到过的权限。
综上所述,我们可以确定,拥有com.apple.private.security.clear-library-validation权限的进程可以使用CLEAR_LV_ENTITLEMENT调用csops系统调用,以清除自身的库验证代码签名标志。即使该进程未以root用户身份运行,这个方法也是有效的。
0x03 SSH
为了确认这个发现,我将具有这个权限的SSH加载到Hopper中,并验证它是否使用csops禁用了库验证。
int sub_10016d41f(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5) { (...) loc_10016d67b: rax = getpid(); rax = csops(rax, 0xf, 0x0, 0x0); if (rax == 0x0) goto loc_10016d6d6; loc_10016d694: rdx = 0x0; rcx = 0x0; rbx = 0x0; sub_10014e556("csops(CS_OPS_CLEAR_LV) failed: %d", rax, rdx, rcx, r8, r9, stack[-136]); (...)
的确,我们可以找到使用操作码0xf调用csops的函数,也就是CS_OPS_CLEAR_LV操作。如果该调用失败,我们还能得到一个详细的错误消息。
0x04 历史
尽管我仅仅在Big Sur版本中发现了这个变化,但是这个功能是较早之前引入的。在xnu-6153.51.1中引入了用于清除库验证标志的详细csops操作,该操作已经在macOS 10.15.2中使用。但是使用该权限的二进制文件仅在macOS 10.15.4版本以后出现,只涉及四个应用程序——su、screen、login和passwd。
从Big Sur(macOS 11.0)开始,有20个二进制文件开始具有这个新的权限,因此Apple逐渐将其迁移到这个新方法上。
0x05 新权限的优势
我们非常好奇,与之前的方法相比,这种方法有哪些优点。在这里我仅仅是推测,但确实发现了一些优势所在。使用新的方法,在加载应用程序时会强行执行库验证,这意味着攻击者无法对这类二进制文件进行dylib劫持或代理攻击。这种攻击方式是非常常见的,特别是针对第三方应用程序。
我们仍然可以将代码注入到这类应用程序中,但只能通过插件的方式,而不能通过其他方式。尽管插件的攻击方式并不比标准dylib方式难,但这种改进已经向更好的设计迈出了一步。遗憾的是,它只适用于Apple自己的文件,如果我们尝试在自己的二进制文件中使用它,则会出现错误。
mac_vnode_check_signature: /tmp/launch: code signature validation failed fatally: When validating /tmp/launch: Code has restricted entitlements, but the validation of its code signature failed. Unsatisfied Entitlements:
0x06 总结
我们看到Apple在macOS 10.15.2中引入了新的权限com.apple.private.security.clear-library-validation,它允许进程使用csops系统调用清除自身的库验证标志。Apple正在将应用程序从旧的com.apple.security.cs.disable-library-validation权限缓慢迁移到新的权限,从而在设计层面上增加安全性。
本文翻译自:https://theevilbit.github.io/posts/com.apple.private.security.clear-library-validation/如若转载,请注明原文地址: