task_for_pid : le Mach trap le plus dangereux de macOS
Comment task_for_pid fonctionne, pourquoi Apple le verrouille, et ce que son modèle d'entitlements implique pour les outils de sécurité macOS.
S'il fallait résumer le modèle de sécurité de macOS en un seul syscall, ce serait task_for_pid. C'est la primitive clé qui, en trois appels, donne à un processus la capacité de lire, écrire et exécuter du code dans n'importe quel autre processus du système. Chaque toolkit d'injection de code, chaque débogueur macOS, chaque EDR à scan mémoire — tous commencent ici.
Apple le sait, c'est pourquoi task_for_pid est l'un des syscalls les plus verrouillés du noyau. Ce post explique comment le trap fonctionne réellement, pourquoi les barrières existent, et ce qui change la donne pour les outils offensifs comme défensifs.
Le trap, mécaniquement
Le prototype est petit :
kern_return_t task_for_pid(
mach_port_name_t target_tport, // mach_task_self()
int pid, // processus cible
mach_port_name_t *t // out : send right sur le task port cible
);
Vous donnez au noyau votre propre task port et un PID cible. En cas de succès, vous récupérez un send right Mach vers le task port de la cible. Avec ce send right, vous pouvez appeler :
mach_vm_read— lire de la mémoire arbitraire dans la cible.mach_vm_write— écrire de la mémoire arbitraire dans la cible.mach_vm_allocate+mach_vm_protect— créer de la nouvelle mémoire RX.thread_create_running— démarrer un nouveau thread à n'importe quel PC.
C'est l'intégralité du toolkit d'injection. Tout ce qui se présente comme une technique d'injection de code sur macOS — injection osascript, injection de dylib, patch de dyld-cache à distance — utilise une variation de ces quatre appels après que task_for_pid ait réussi.
Comment la barrière fonctionne
Dans XNU, task_for_pid() finit dans task_for_pid_posix_check() (dans bsd/vm/vm_unix.c). Les vérifications, dans leur ordre :
- Soi-même ? Si
pid == getpid(), l'appel réussit toujours. - Root + même session d'audit ? Un appelant root dans la même session d'audit que la cible réussit inconditionnellement.
- Hook de politique AMFI. AppleMobileFileIntegrity vote. Si l'appelant a l'entitlement
com.apple.system-task-ports(réservé Apple), succès. - Entitlement débogueur ? Si l'appelant a
com.apple.security.cs.debuggeret que la cible aget-task-allow, succès. C'est le chemin Xcode pour le debug. - Politique MACF. Le framework MACF (où branchent TCC et Endpoint Security) a le veto final.
- Sinon :
KERN_FAILURE. Loggé dans/var/log/system.logsous « Security policy would not allow process ».
Conclusion : un appelant non-root sans entitlement ne peut obtenir un task port que pour son propre processus, ou pour un enfant debug-allowed qu'il a lui-même lancé. Tout le reste échoue par conception.
Le raccourci Developer Tools
Réglages Système → Confidentialité et sécurité → Outils de développement est le chemin utilisateur pour relâcher la barrière. Quand un utilisateur y ajoute un processus, TCC enregistre une exception par-processus qui satisfait MACF à l'étape 5. C'est ainsi que Charles Proxy, Wireshark et la plupart des outils de profilage convainquent le noyau de leur remettre des task ports.
Pour l'automatisation, l'équivalent est le plist taskgated-helper et le droit d'autorisation system.privilege.taskport. Terrain connu pour les pipelines de build.
Pourquoi task_name_for_pid est le cousin « sûr »
Il existe un syscall sœur, task_name_for_pid, au nom trompeusement proche et à la capacité bien plus faible. Il retourne un port en nom seulement — vous pouvez lire certaines infos comptables (PID, jeton d'audit, état basique) mais pas la mémoire, ni écrire, ni changer l'état des threads. Apple l'a introduit précisément pour que les outils de monitoring puissent énumérer les processus sans déclencher la lourde barrière task_for_pid.
Deux cousins introduits dans macOS 11+ vont plus loin :
task_read_for_pid— port en lecture seule. Peutmach_vm_readmais pasmach_vm_write. Utilisé parsample,vmmap, et le Time Profiler.task_inspect_for_pid— encore plus étroit ; ne lit que les statistiques.
Si vous écrivez un outil qui doit introspecter un autre processus sans écrire, préférez ces variantes. Elles survivent mieux au resserrement du sandbox et déclenchent moins d'alertes.
Ce que font réellement les attaquants
Les rapports publics 2024–2026 montrent deux schémas courants :
-
task_for_piden direct contre des PIDs non liés. Le classique. DazzleSpy, JokerSpy, ChromeLoader-mac, NotLockBit-mac — tous chaînenttask_for_pid+mach_vm_write+thread_create_running. Ça marche quand le malware tourne en root (souvent via un LaunchDaemon malicieux) ou quand l'utilisateur a été socialement piégé pour ajouter le helper aux Outils de développement. -
Abus d'entitlement via binaires signés livrés. Trouver un binaire Apple-signé avec
com.apple.system-task-portset le shipper comme « helper ». Les travaux 2023 surFind Any Fileet plus anciens sur le helper suid deTunnelblicken sont des exemples publics. Apple révoque les signatures à mesure qu'ils sont repérés. -
debug_control_port_for_pid(plus récent). Introduit dans macOS 12, ce Mach trap retourne untask_control_portaux débogueurs entitlés, distinct du legacytask_for_pid. C'est désormais le chemin de Xcode sur Apple Silicon. Tout malware qui veut se faire passer pour un débogueur viendra ici s'il peut gagner l'entitlement.
Ce que les défenseurs doivent surveiller
Endpoint Security expose les événements clés :
ES_EVENT_TYPE_AUTH_GET_TASK— se déclenche avant la décision ; vous pouvez refuser.ES_EVENT_TYPE_NOTIFY_GET_TASK— se déclenche après le succès ; vous pouvez enregistrer sans bloquer.ES_EVENT_TYPE_AUTH_GET_TASK_NAMEet_NOTIFY_GET_TASK_NAME— pour les cousins légers.
Règle de détection pratique : tout NOTIFY_GET_TASK dont l'appelant n'est pas un débogueur système (lldb-rpc-server, Xcode.app/Contents/MacOS/Xcode, Activity Monitor, sample, vmmap) et dont la cible est WindowServer, launchd, loginwindow, ou un processus navigateur. Le taux de faux positifs est étonnamment bas.
Si vous construisez de l'outillage là-dessus, le post suivant, Détecter l'abus de syscalls avec macOS Endpoint Security en 2026, parcourt le côté ES de bout en bout.
Pour aller plus loin
- La page de référence complète
task_for_pidcontient le prototype, l'historique des versions et les stubs arm64/x86_64. - Comment fonctionnent les syscalls macOS en 2026 couvre la mécanique du trap.
- Mach traps vs syscalls BSD explique pourquoi celui-ci vit côté Mach.