macOS MACF

Reading time: 13 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks

基本信息

MACF 代表 强制访问控制框架,这是一个内置于操作系统的安全系统,用于帮助保护您的计算机。它通过设置 关于谁或什么可以访问系统某些部分的严格规则 来工作,例如文件、应用程序和系统资源。通过自动执行这些规则,MACF 确保只有授权用户和进程可以执行特定操作,从而降低未经授权访问或恶意活动的风险。

请注意,MACF 并不真正做出任何决策,因为它只是 拦截 操作,它将决策留给它调用的 策略模块(内核扩展),例如 AppleMobileFileIntegrity.kextQuarantine.kextSandbox.kextTMSafetyNet.kextmcxalr.kext

流程

  1. 进程执行 syscall/mach trap
  2. 内核内部调用相关函数
  3. 函数调用 MACF
  4. MACF 检查请求在其策略中挂钩该函数的策略模块
  5. MACF 调用相关策略
  6. 策略指示是否允许或拒绝该操作

caution

只有 Apple 可以使用 MAC 框架 KPI。

标签

MACF 使用 标签,然后策略会检查是否应该授予某些访问权限。标签结构声明的代码可以在 这里 找到,该代码随后在 这里struct ucred 中使用,位于 cr_label 部分。标签包含标志和可由 MACF 策略分配指针 数量。例如,Sandbox 将指向容器配置文件。

MACF 策略

MACF 策略定义了 在某些内核操作中应用的规则和条件

内核扩展可以配置 mac_policy_conf 结构,然后通过调用 mac_policy_register 注册它。从 这里:

c
#define mpc_t	struct mac_policy_conf *

/**
@brief Mac policy configuration

This structure specifies the configuration information for a
MAC policy module.  A policy module developer must supply
a short unique policy name, a more descriptive full name, a list of label
namespaces and count, a pointer to the registered enty point operations,
any load time flags, and optionally, a pointer to a label slot identifier.

The Framework will update the runtime flags (mpc_runtime_flags) to
indicate that the module has been registered.

If the label slot identifier (mpc_field_off) is NULL, the Framework
will not provide label storage for the policy.  Otherwise, the
Framework will store the label location (slot) in this field.

The mpc_list field is used by the Framework and should not be
modified by policies.
*/
/* XXX - reorder these for better aligment on 64bit platforms */
struct mac_policy_conf {
const char		*mpc_name;		/** policy name */
const char		*mpc_fullname;		/** full name */
const char		**mpc_labelnames;	/** managed label namespaces */
unsigned int		 mpc_labelname_count;	/** number of managed label namespaces */
struct mac_policy_ops	*mpc_ops;		/** operation vector */
int			 mpc_loadtime_flags;	/** load time flags */
int			*mpc_field_off;		/** label slot */
int			 mpc_runtime_flags;	/** run time flags */
mpc_t			 mpc_list;		/** List reference */
void			*mpc_data;		/** module data */
};

很容易通过检查对 mac_policy_register 的调用来识别配置这些策略的内核扩展。此外,通过检查扩展的反汇编,也可以找到使用的 mac_policy_conf 结构。

请注意,MACF 策略也可以动态注册和注销。

mac_policy_conf 的主要字段之一是 mpc_ops。该字段指定策略感兴趣的操作。请注意,它们有数百个,因此可以将所有操作置为零,然后仅选择策略感兴趣的操作。从 here:

c
struct mac_policy_ops {
mpo_audit_check_postselect_t		*mpo_audit_check_postselect;
mpo_audit_check_preselect_t		*mpo_audit_check_preselect;
mpo_bpfdesc_label_associate_t		*mpo_bpfdesc_label_associate;
mpo_bpfdesc_label_destroy_t		*mpo_bpfdesc_label_destroy;
mpo_bpfdesc_label_init_t		*mpo_bpfdesc_label_init;
mpo_bpfdesc_check_receive_t		*mpo_bpfdesc_check_receive;
mpo_cred_check_label_update_execve_t	*mpo_cred_check_label_update_execve;
mpo_cred_check_label_update_t		*mpo_cred_check_label_update;
[...]

几乎所有的钩子在拦截这些操作时都会被 MACF 回调。然而,mpo_policy_* 钩子是一个例外,因为 mpo_hook_policy_init() 是在注册时调用的回调(即在 mac_policy_register() 之后),而 mpo_hook_policy_initbsd() 是在 BSD 子系统正确初始化后进行晚期注册时调用的。

此外,任何 kext 都可以注册 mpo_policy_syscall 钩子,以暴露一个私有的 ioctl 风格调用 接口。然后,用户客户端将能够调用 mac_syscall (#381),并指定 策略名称、一个整数 代码 和可选的 参数 作为参数。
例如,Sandbox.kext 经常使用这个。

检查 kext 的 __DATA.__const* 可以识别在注册策略时使用的 mac_policy_ops 结构。可以找到它,因为它的指针在 mpo_policy_conf 内部的一个偏移量处,并且因为该区域内将有许多 NULL 指针。

此外,还可以通过从内存中转储结构 _mac_policy_list 来获取已配置策略的 kext 列表,该结构会随着每个注册的策略而更新。

MACF 初始化

MACF 很快就会初始化。它在 XNU 的 bootstrap_thread 中设置:在 ipc_bootstrap 之后调用 mac_policy_init(),该函数初始化 mac_policy_list,随后调用 mac_policy_initmach()。除了其他功能外,该函数将获取所有在其 Info.plist 中具有 AppleSecurityExtension 键的 Apple kext,如 ALF.kextAppleMobileFileIntegrity.kextQuarantine.kextSandbox.kextTMSafetyNet.kext 并加载它们。

MACF 回调

在代码中常常可以找到对 MACF 的回调定义,例如:#if CONFIG_MAC 条件块。此外,在这些块内可以找到对 mac_proc_check* 的调用,该调用会调用 MACF 来 检查权限 以执行某些操作。此外,MACF 回调的格式为:mac_<object>_<opType>_opName

对象是以下之一:bpfdesccredfileprocvnodemountdevfsifnetinpcbmbufipqpipesysv[msg/msq/shm/sem]posix[shm/sem]socketkext
opType 通常是 check,用于允许或拒绝该操作。然而,也可以找到 notify,这将允许 kext 对给定操作做出反应。

您可以在 https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/kern_mman.c#L621 中找到一个示例:

int
mmap(proc_t p, struct mmap_args *uap, user_addr_t *retval)
{
[...]
#if CONFIG_MACF
			error = mac_file_check_mmap(vfs_context_ucred(ctx),
			    fp->fp_glob, prot, flags, file_pos + pageoff,
&maxprot);
if (error) {
(void)vnode_put(vp);
goto bad;
}
#endif /* MAC */
[...]

然后,可以在 https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/security/mac_file.c#L174 中找到 mac_file_check_mmap 的代码。

c
mac_file_check_mmap(struct ucred *cred, struct fileglob *fg, int prot,
int flags, uint64_t offset, int *maxprot)
{
int error;
int maxp;

maxp = *maxprot;
MAC_CHECK(file_check_mmap, cred, fg, NULL, prot, flags, offset, &maxp);
if ((maxp | *maxprot) != *maxprot) {
panic("file_check_mmap increased max protections");
}
*maxprot = maxp;
return error;
}

调用 MAC_CHECK 宏,其代码可以在 https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/security/mac_internal.h#L261 找到。

c
/*
* MAC_CHECK performs the designated check by walking the policy
* module list and checking with each as to how it feels about the
* request.  Note that it returns its value via 'error' in the scope
* of the caller.
*/
#define MAC_CHECK(check, args...) do {                              \
error = 0;                                                      \
MAC_POLICY_ITERATE({                                            \
if (mpc->mpc_ops->mpo_ ## check != NULL) {              \
DTRACE_MACF3(mac__call__ ## check, void *, mpc, int, error, int, MAC_ITERATE_CHECK); \
int __step_err = mpc->mpc_ops->mpo_ ## check (args); \
DTRACE_MACF2(mac__rslt__ ## check, void *, mpc, int, __step_err); \
error = mac_error_select(__step_err, error);         \
}                                                           \
});                                                             \
} while (0)

将遍历所有注册的 mac 策略,调用它们的函数并将输出存储在 error 变量中,该变量只能通过成功代码的 mac_error_select 进行覆盖,因此如果任何检查失败,整个检查将失败,操作将不被允许。

tip

然而,请记住,并非所有 MACF 调用仅用于拒绝操作。例如,mac_priv_grant 调用宏 MAC_GRANT,如果任何策略返回 0,则将授予请求的特权:

/*
 * MAC_GRANT 执行指定的检查,通过遍历策略
 * 模块列表并与每个模块检查其对请求的看法。
 * 与 MAC_CHECK 不同,如果任何策略返回 '0',则授予,
 * 否则返回 EPERM。请注意,它通过
 * 调用者的作用域中的 'error' 返回其值。
 */
#define MAC_GRANT(check, args...) do {                              \
    error = EPERM;                                                  \
    MAC_POLICY_ITERATE({                                            \
	if (mpc->mpc_ops->mpo_ ## check != NULL) {                  \
	        DTRACE_MACF3(mac__call__ ## check, void *, mpc, int, error, int, MAC_ITERATE_GRANT); \
	        int __step_res = mpc->mpc_ops->mpo_ ## check (args); \
	        if (__step_res == 0) {                              \
	                error = 0;                                  \
	        }                                                   \
	        DTRACE_MACF2(mac__rslt__ ## check, void *, mpc, int, __step_res); \
	    }                                                           \
    });                                                             \
} while (0)

priv_check & priv_grant

这些调用旨在检查和提供在 bsd/sys/priv.h 中定义的(数十个)特权
一些内核代码会使用进程的 KAuth 凭据调用 bsd/kern/kern_priv.c 中的 priv_check_cred(),并使用特权代码调用 mac_priv_check 以查看是否有任何策略 拒绝 授予特权,然后调用 mac_priv_grant 以查看是否有任何策略授予该 privilege

proc_check_syscall_unix

此钩子允许拦截所有系统调用。在 bsd/dev/[i386|arm]/systemcalls.c 中,可以看到声明的函数 unix_syscall,其中包含此代码:

c
#if CONFIG_MACF
if (__improbable(proc_syscall_filter_mask(proc) != NULL && !bitstr_test(proc_syscall_filter_mask(proc), syscode))) {
error = mac_proc_check_syscall_unix(proc, syscode);
if (error) {
goto skip_syscall;
}
}
#endif /* CONFIG_MACF */

将检查调用进程的 bitmask,以确定当前的系统调用是否应该调用 mac_proc_check_syscall_unix。这是因为系统调用的频率很高,因此避免每次都调用 mac_proc_check_syscall_unix 是很有意义的。

请注意,函数 proc_set_syscall_filter_mask(),用于设置进程中的 bitmask 系统调用,是由 Sandbox 调用以在沙箱进程上设置掩码的。

暴露的 MACF 系统调用

可以通过在 security/mac.h 中定义的一些系统调用与 MACF 进行交互:

c
/*
* Extended non-POSIX.1e interfaces that offer additional services
* available from the userland and kernel MAC frameworks.
*/
#ifdef __APPLE_API_PRIVATE
__BEGIN_DECLS
int      __mac_execve(char *fname, char **argv, char **envv, mac_t _label);
int      __mac_get_fd(int _fd, mac_t _label);
int      __mac_get_file(const char *_path, mac_t _label);
int      __mac_get_link(const char *_path, mac_t _label);
int      __mac_get_pid(pid_t _pid, mac_t _label);
int      __mac_get_proc(mac_t _label);
int      __mac_set_fd(int _fildes, const mac_t _label);
int      __mac_set_file(const char *_path, mac_t _label);
int      __mac_set_link(const char *_path, mac_t _label);
int      __mac_mount(const char *type, const char *path, int flags, void *data,
struct mac *label);
int      __mac_get_mount(const char *path, struct mac *label);
int      __mac_set_proc(const mac_t _label);
int      __mac_syscall(const char *_policyname, int _call, void *_arg);
__END_DECLS
#endif /*__APPLE_API_PRIVATE*/

参考

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks