macOS MACF
Reading time: 13 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
MACF 代表 强制访问控制框架,这是一个内置于操作系统的安全系统,用于帮助保护您的计算机。它通过设置 关于谁或什么可以访问系统某些部分的严格规则 来工作,例如文件、应用程序和系统资源。通过自动执行这些规则,MACF 确保只有授权用户和进程可以执行特定操作,从而降低未经授权访问或恶意活动的风险。
请注意,MACF 并不真正做出任何决策,因为它只是 拦截 操作,它将决策留给它调用的 策略模块(内核扩展),例如 AppleMobileFileIntegrity.kext
、Quarantine.kext
、Sandbox.kext
、TMSafetyNet.kext
和 mcxalr.kext
。
流程
- 进程执行 syscall/mach trap
- 内核内部调用相关函数
- 函数调用 MACF
- MACF 检查请求在其策略中挂钩该函数的策略模块
- MACF 调用相关策略
- 策略指示是否允许或拒绝该操作
caution
只有 Apple 可以使用 MAC 框架 KPI。
标签
MACF 使用 标签,然后策略会检查是否应该授予某些访问权限。标签结构声明的代码可以在 这里 找到,该代码随后在 这里 的 struct ucred
中使用,位于 cr_label
部分。标签包含标志和可由 MACF 策略分配指针 的 槽 数量。例如,Sandbox 将指向容器配置文件。
MACF 策略
MACF 策略定义了 在某些内核操作中应用的规则和条件。
内核扩展可以配置 mac_policy_conf
结构,然后通过调用 mac_policy_register
注册它。从 这里:
#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:
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.kext
、AppleMobileFileIntegrity.kext
、Quarantine.kext
、Sandbox.kext
和 TMSafetyNet.kext
并加载它们。
MACF 回调
在代码中常常可以找到对 MACF 的回调定义,例如:#if CONFIG_MAC
条件块。此外,在这些块内可以找到对 mac_proc_check*
的调用,该调用会调用 MACF 来 检查权限 以执行某些操作。此外,MACF 回调的格式为:mac_<object>_<opType>_opName
。
对象是以下之一:bpfdesc
、cred
、file
、proc
、vnode
、mount
、devfs
、ifnet
、inpcb
、mbuf
、ipq
、pipe
、sysv[msg/msq/shm/sem]
、posix[shm/sem]
、socket
、kext
。
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
的代码。
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 找到。
/*
* 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
,其中包含此代码:
#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 进行交互:
/*
* 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
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。