
Userland exec PoC to be used as attack vector technique
L'exécution en espace utilisateur remplace l'image du processus existant dans l'espace d'adressage courant par une nouvelle. Elle imite le comportement de l'appel système execve, mais les structures de processus décrivant l'image du processus restent inchangées. En d'autres termes, le nom de processus rapporté par les utilitaires système conservera l'ancien nom de processus.
Cette technique peut être utilisée pour rester furtif après avoir obtenu une exécution de code arbitraire. Elle peut également être utilisée pour exécuter des binaires stockés dans des partitions noexec.
La première exécution en espace utilisateur a été créée par grugq. Ce dépôt s'inspire fortement de la bibliothèque Mettle de Rapid7, qui comprend une description complète de la technique sur son blog.
Au départ, une grande partie du code de ce dépôt imitait la bibliothèque Mettle, mais il a depuis été étendu pour inclure une complexité supplémentaire afin de contourner la vérification SELinux.
SELinux inclut la vérification execmem, qui garantit :
PROT_WRITE à PROT_EXEC via mprotect est interdit).mprotectPour contourner mprotect, il est nécessaire de créer un fichier temporaire. Cela peut être réalisé en utilisant memfd_create combiné avec munmap et mmap, évitant ainsi complètement l'appel système mprotect.
L'exemple elf_debugger.c démontre que tout ELF contient une région PT_LOAD à la fois exécutable et accessible en écriture. Cette région est nécessaire pour charger les informations du programme pendant l'exécution. Pour résoudre ce problème, l'implémentation bypass_wx.c a été créée. Cette conception :
SIGSEGV lors des tentatives d'écriture sur cette page.PROT_EXEC à PROT_WRITE.Cette section décrit comment compiler pour les machines Android et x86. Assurez-vous que libelf est installé avant de continuer.
mkdir build && cd build
cmake ..
make
desktop % strace ./uexec hello others args here 2>&1 | grep exec
execve("./uexec", ["./uexec", "hello", "others", "args", "here"], 0x7ffc34ec02f0 /* 54 vars */) = 0
desktop % strace bash -c ./hello 2>&1 | grep exec
execve("/usr/bin/bash", ["bash", "-c", "./hello"], 0x7ffebecc3130 /* 54 vars */) = 0
newfstatat(AT_FDCWD, "/desktop/userland-exec/build", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/desktop/userland-exec", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
execve("./hello", ["./hello"], 0x5fb22658e2a0 /* 54 vars */) = 0
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 ..
make
desktop % adb push uexec hello /data/local/tmp
uexec: 1 file pushed, 0 skipped. 113.9 MB/s (22912 bytes in 0.000s)
hello: 1 file pushed, 0 skipped. 184.9 MB/s (6936 bytes in 0.000s)
2 files pushed, 0 skipped. 0.3 MB/s (29848 bytes in 0.090s)
desktop % adb shell
dm3q:/ $ cd /data/local/tmp
dm3q:/data/local/tmp $ chmod +x uexec
dm3q:/data/local/tmp $ ./hello
Hello World
dm3q:/data/local/tmp $ ./uexec hello
Hello World
dm3q:/data/local/tmp $
Sur CentOS, la bibliothèque libc peut présenter un comportement inhabituel. Pour résoudre ce problème, un simple programme « Hello, World » écrit en assembleur, hello_nolibc.s, est fourni. Cet exemple, avec le cmake, montre comment compiler et exécuter un programme sans lier à libc.
Si votre CMAKE est supérieur à 4.0, vous pouvez ajouter dans la compilation -DCMAKE_POLICY_VERSION_MINIMUM=3.5.
Ce dépôt utilise la licence GPL-3.0.
| Système d'exploitation | Architecture | Résultat |
|---|
| Ubuntu 24.04 | x86_64 | Succès |
| Archlinux 6.12.4 | x86_64 | Succès |
| CentOS | x86_64 | Succès |
| Raspberry Pi OS | arm64 | Succès |
| S23 Android 14 | arm64 | Succès |