macOS MACF
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Información Básica
MACF significa Marco de Control de Acceso Obligatorio, que es un sistema de seguridad integrado en el sistema operativo para ayudar a proteger tu computadora. Funciona estableciendo reglas estrictas sobre quién o qué puede acceder a ciertas partes del sistema, como archivos, aplicaciones y recursos del sistema. Al hacer cumplir estas reglas automáticamente, MACF asegura que solo los usuarios y procesos autorizados puedan realizar acciones específicas, reduciendo el riesgo de acceso no autorizado o actividades maliciosas.
Ten en cuenta que MACF realmente no toma decisiones, ya que solo intercepta acciones, deja las decisiones a los módulos de política (extensiones del kernel) que llama como AppleMobileFileIntegrity.kext
, Quarantine.kext
, Sandbox.kext
, TMSafetyNet.kext
y mcxalr.kext
.
Flujo
- El proceso realiza una llamada al syscall/trampa mach
- La función relevante se llama dentro del kernel
- La función llama a MACF
- MACF verifica los módulos de política que solicitaron enganchar esa función en su política
- MACF llama a las políticas relevantes
- Las políticas indican si permiten o deniegan la acción
caution
Apple es el único que puede usar el KPI del Marco MAC.
Etiquetas
MACF utiliza etiquetas que luego las políticas comprobarán si deben otorgar algún acceso o no. El código de la declaración de la estructura de etiquetas se puede encontrar aquí, que se utiliza dentro de la struct ucred
en aquí en la parte de cr_label
. La etiqueta contiene flags y un número de slots que pueden ser utilizados por políticas MACF para asignar punteros. Por ejemplo, Sanbox apuntará al perfil del contenedor.
Políticas MACF
Una Política MACF define reglas y condiciones que se aplicarán en ciertas operaciones del kernel.
Una extensión del kernel podría configurar una estructura mac_policy_conf
y luego registrarla llamando a mac_policy_register
. Desde aquí:
#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 */
};
Es fácil identificar las extensiones del kernel que configuran estas políticas al verificar las llamadas a mac_policy_register
. Además, al revisar el desensamblado de la extensión, también es posible encontrar la estructura mac_policy_conf
utilizada.
Tenga en cuenta que las políticas MACF también se pueden registrar y anular dinámicamente.
Uno de los campos principales de mac_policy_conf
es mpc_ops
. Este campo especifica qué operaciones le interesan a la política. Tenga en cuenta que hay cientos de ellas, por lo que es posible establecer todas en cero y luego seleccionar solo las que le interesan a la política. Desde aquí:
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;
[...]
Casi todos los hooks serán llamados por MACF cuando una de esas operaciones sea interceptada. Sin embargo, los hooks mpo_policy_*
son una excepción porque mpo_hook_policy_init()
es un callback llamado al registrarse (después de mac_policy_register()
) y mpo_hook_policy_initbsd()
se llama durante el registro tardío una vez que el subsistema BSD se ha inicializado correctamente.
Además, el hook mpo_policy_syscall
puede ser registrado por cualquier kext para exponer una interfaz de llamada de estilo ioctl privada. Luego, un cliente de usuario podrá llamar a mac_syscall
(#381) especificando como parámetros el nombre de la política con un código entero y argumentos opcionales.
Por ejemplo, el Sandbox.kext
utiliza esto mucho.
Revisando el __DATA.__const*
del kext es posible identificar la estructura mac_policy_ops
utilizada al registrar la política. Es posible encontrarla porque su puntero está en un desplazamiento dentro de mpo_policy_conf
y también debido a la cantidad de punteros NULL que habrá en esa área.
Además, también es posible obtener la lista de kexts que han configurado una política volcando de la memoria la estructura _mac_policy_list
que se actualiza con cada política que se registra.
Inicialización de MACF
MACF se inicializa muy pronto. Se configura en el bootstrap_thread
de XNU: después de ipc_bootstrap
se llama a mac_policy_init()
que inicializa la mac_policy_list
y momentos después se llama a mac_policy_initmach()
. Entre otras cosas, esta función obtendrá todos los kexts de Apple con la clave AppleSecurityExtension
en su Info.plist como ALF.kext
, AppleMobileFileIntegrity.kext
, Quarantine.kext
, Sandbox.kext
y TMSafetyNet.kext
y los carga.
Llamadas de MACF
Es común encontrar llamadas a MACF definidas en el código como: #if CONFIG_MAC
bloques condicionales. Además, dentro de estos bloques es posible encontrar llamadas a mac_proc_check*
que llaman a MACF para verificar permisos para realizar ciertas acciones. Además, el formato de las llamadas de MACF es: mac_<object>_<opType>_opName
.
El objeto es uno de los siguientes: bpfdesc
, cred
, file
, proc
, vnode
, mount
, devfs
, ifnet
, inpcb
, mbuf
, ipq
, pipe
, sysv[msg/msq/shm/sem]
, posix[shm/sem]
, socket
, kext
.
El opType
suele ser check que se utilizará para permitir o denegar la acción. Sin embargo, también es posible encontrar notify
, que permitirá al kext reaccionar a la acción dada.
Puedes encontrar un ejemplo en 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 */
[...]
Luego, es posible encontrar el código de mac_file_check_mmap
en https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/security/mac_file.c#L174
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;
}
El cual llama al macro MAC_CHECK
, cuyo código se puede encontrar en 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)
Que revisará todas las políticas de mac registradas llamando a sus funciones y almacenando la salida dentro de la variable de error, que solo será sobreescribible por mac_error_select
mediante códigos de éxito, por lo que si alguna verificación falla, la verificación completa fallará y la acción no será permitida.
tip
Sin embargo, recuerda que no todos los llamados de MACF se utilizan solo para denegar acciones. Por ejemplo, mac_priv_grant
llama al macro MAC_GRANT, que otorgará el privilegio solicitado si alguna política responde con un 0:
/*
* MAC_GRANT realiza la verificación designada al recorrer la lista de
* módulos de políticas y consultando con cada uno sobre cómo se siente
* respecto a la solicitud. A diferencia de MAC_CHECK, otorga si
* alguna política devuelve '0', y de lo contrario devuelve EPERM.
* Ten en cuenta que devuelve su valor a través de 'error' en el
* ámbito del llamador.
*/
#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
Estas llamadas están destinadas a verificar y proporcionar (decenas de) privilegios definidos en bsd/sys/priv.h.
Algún código del kernel llamaría a priv_check_cred()
desde bsd/kern/kern_priv.c con las credenciales KAuth del proceso y uno de los códigos de privilegios que llamará a mac_priv_check
para ver si alguna política niega otorgar el privilegio y luego llama a mac_priv_grant
para ver si alguna política otorga el privilegio
.
proc_check_syscall_unix
Este gancho permite interceptar todas las llamadas al sistema. En bsd/dev/[i386|arm]/systemcalls.c
es posible ver la función declarada unix_syscall
, que contiene este código:
#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 */
Que verificará en el proceso de llamada bitmask si la syscall actual debería llamar a mac_proc_check_syscall_unix
. Esto se debe a que las syscalls se llaman con tanta frecuencia que es interesante evitar llamar a mac_proc_check_syscall_unix
cada vez.
Tenga en cuenta que la función proc_set_syscall_filter_mask()
, que establece la bitmask de syscalls en un proceso, es llamada por Sandbox para establecer máscaras en procesos en sandbox.
Syscalls MACF expuestas
Es posible interactuar con MACF a través de algunas syscalls definidas en security/mac.h:
/*
* 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*/
Referencias
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.