
Userland exec PoC to be used as attack vector technique
Userland exec reemplaza la imagen del proceso existente dentro del espacio de direcciones actual por una nueva. Imita el comportamiento de la llamada al sistema execve, pero las estructuras del proceso que describen la imagen del proceso permanecen sin cambios. En otras palabras, el nombre del proceso reportado por las utilidades del sistema conservará el nombre del proceso anterior.
Esta técnica se puede utilizar para lograr sigilo después de obtener ejecución arbitraria de código. También se puede utilizar para ejecutar binarios almacenados en particiones noexec.
El primer userland exec fue creado por grugq. Este repositorio está fuertemente inspirado en la librería Mettle de Rapid7, que incluye una descripción completa en su blog de la técnica.
Inicialmente, gran parte del código de este repositorio imitaba a la librería Mettle, pero desde entonces se ha extendido para incluir complejidad adicional y así evadir la verificación de SELinux.
SELinux incluye la verificación execmem, que garantiza:
PROT_WRITE a PROT_EXEC usando mprotect está prohibido).mprotectPara evadir mprotect, es necesario crear un archivo temporal. Esto se puede lograr usando memfd_create combinado con munmap y mmap, evitando así por completo la llamada al sistema mprotect.
El ejemplo elf_debugger.c demuestra que cualquier ELF contiene una región PT_LOAD que es a la vez ejecutable y escribible. Esta región es necesaria para cargar la información del programa durante la ejecución. Para solucionar esto, se creó la implementación bypass_wx.c. Este diseño:
SIGSEGV.PROT_EXEC a PROT_WRITE.Esta sección describe cómo compilar para máquinas Android y x86. Asegúrate de tener libelf instalado antes de continuar.
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 $
En CentOS, la librería libc puede presentar un comportamiento inusual. Para solucionar este problema, se ha proporcionado un sencillo programa "Hello, World" escrito en ensamblador, hello_nolibc.s. Este ejemplo junto con el cmake demuestra cómo compilar y ejecutar un programa sin enlazar con libc.
Si tu CMAKE es superior a 4.0, puedes añadir en la compilación -DCMAKE_POLICY_VERSION_MINIMUM=3.5.
Este repositorio utiliza la Licencia GPL-3.0.
| SO | Arquitectura | Resultado |
|---|
| Ubuntu 24.04 | x86_64 | Éxito |
| Archlinux 6.12.4 | x86_64 | Éxito |
| CentOS | x86_64 | Éxito |
| Raspberry Pi OS | arm64 | Éxito |
| S23 Android 14 | arm64 | Éxito |