深入分析macOS签名验证权限com.apple.private.security.clear-library-validation - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

深入分析macOS签名验证权限com.apple.private.security.clear-library-validation

41yf1sh 资讯 2021-02-23 11:00:00
296201
收藏

导语:使用新的方法,在加载应用程序时会强行执行库验证,这意味着攻击者无法对这类二进制文件进行dylib劫持或代理攻击。

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/如若转载,请注明原文地址:
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务