
Пользовательский PoC для exec, используемый как техника вектора атаки
Userland exec заменяет существующий образ процесса в текущем адресном пространстве новым. Он имитирует поведение системного вызова execve, но структуры процесса, описывающие образ процесса, остаются неизменными. Иными словами, имя процесса, отображаемое системными утилитами, сохранит прежнее имя процесса.
Эта техника может использоваться для обеспечения скрытности после получения произвольного выполнения кода. Её также можно использовать для запуска бинарных файлов, хранящихся в разделах noexec.
Первый userland exec был создан grugq. Этот репозиторий во многом вдохновлён библиотекой Rapid7 Mettle, в которой есть подробное описание в блоге этой техники.
Изначально большая часть кода этого репозитория повторяла библиотеку Mettle, но с тех пор он был расширен дополнительной логикой для обхода проверок SELinux.
SELinux включает проверку execmem, которая гарантирует:
PROT_WRITE на PROT_EXEC с помощью запрещено).mprotectmprotectЧтобы обойти mprotect, необходимо создать временный файл. Это можно сделать с помощью memfd_create в сочетании с munmap и mmap, полностью избегая системного вызова mprotect.
Пример elf_debugger.c демонстрирует, что любой ELF-файл содержит область PT_LOAD, которая одновременно является исполняемой и записываемой. Эта область необходима для загрузки информации о программе во время выполнения. Для решения этой проблемы была создана реализация bypass_wx.c. Эта конструкция:
SIGSEGV.PROT_EXEC на PROT_WRITE.| OS | Архитектура | Результат |
|---|---|---|
| Ubuntu 24.04 | x86_64 | Успешно |
| Archlinux 6.12.4 | x86_64 | Успешно |
| CentOS | x86_64 | Успешно |
| Raspberry Pi OS | arm64 | Успешно |
| S23 Android 14 | arm64 | Успешно |
В этом разделе описана сборка для устройств Android и машин x86. Перед началом убедитесь, что установлен libelf.
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 $
В CentOS библиотека libc может вести себя необычно. Для решения этой проблемы предоставлена простая программа "Hello, World" на ассемблере — hello_nolibc.s. Этот пример вместе с cmake демонстрирует, как собрать и запустить программу без связывания с libc.
Если ваша версия CMAKE выше 4.0, при сборке можно добавить -DCMAKE_POLICY_VERSION_MINIMUM=3.5.
Этот репозиторий использует лицензию GPL-3.0.