
PoC de execução em userland para ser usado como técnica de vetor de ataque
O Userland exec substitui a imagem do processo existente dentro do espaço de endereçamento atual por uma nova. Ele imita o comportamento da chamada de sistema execve, mas as estruturas de processo que descrevem a imagem do processo permanecem inalteradas. Em outras palavras, o nome do processo relatado pelos utilitários do sistema manterá o nome antigo do processo.
Esta técnica pode ser usada para alcançar furtividade após obter execução arbitrária de código. Também pode ser usada para executar binários armazenados em partições noexec.
O primeiro userland exec foi criado por grugq. Este repositório é fortemente inspirado na biblioteca Mettle do Rapid7, que inclui uma descrição abrangente em blog sobre a técnica.
Inicialmente, grande parte do código deste repositório imitava a biblioteca Mettle, mas desde então foi estendido para incluir complexidade adicional para contornar a verificação do SELinux.
O SELinux inclui a verificação execmem, que garante:
PROT_WRITE para PROT_EXEC usando mprotect é proibido).mprotectPara contornar o mprotect, é necessário criar um arquivo temporário. Isso pode ser alcançado usando memfd_create combinado com munmap e mmap, evitando assim a chamada de sistema mprotect por completo.
O exemplo elf_debugger.c demonstra que qualquer ELF contém uma região PT_LOAD que é simultaneamente executável e gravável. Essa região é necessária para carregar as informações do programa durante a execução. Para resolver isso, foi criada a implementação bypass_wx.c. Este design:
SIGSEGV.PROT_EXEC para PROT_WRITE.| SO | Arquitetura | Resultado |
|---|---|---|
| Ubuntu 24.04 | x86_64 | Sucesso |
| Archlinux 6.12.4 | x86_64 | Sucesso |
| CentOS | x86_64 | Sucesso |
| Raspberry Pi OS | arm64 | Sucesso |
| S23 Android 14 | arm64 | Sucesso |
Esta seção descreve como compilar para máquinas Android e x86. Certifique-se de que o libelf esteja 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 $
No CentOS, a biblioteca libc pode apresentar comportamento incomum. Para resolver esse problema, um simples programa "Hello, World" escrito em assembly, hello_nolibc.s, foi fornecido. Este exemplo, juntamente com o cmake, demonstra como compilar e executar um programa sem vincular à libc.
Se o seu CMAKE estiver acima da versão 4.0, você pode adicionar no build -DCMAKE_POLICY_VERSION_MINIMUM=3.5.
Este repositório usa a Licença GPL-3.0.