ulexecve :Linux 上从用户态执行任意 ELF 二进制文件

作者:Sec-Labs | 发布时间:

项目地址

https://github.com/anvilsecure/ulexecve

项目介绍

ulexecve 是一个用户态 execve() 实现,它可以帮助您在 Linux 上从用户态执行任意 ELF 二进制文件,而不必让二进制文件接触存储。这对于红队和反取证目的很有用。

快速开始

无需调用 execve() 即可执行动态或静态编译的 ELF Linux 二进制文件。

cat /bin/echo | ulexecve - hello
hello

介绍

这个 Python 工具被调用 ulexecve ,它代表 userland execve 。 它可以帮助您从用户空间在 Linux 系统上执行任意 ELF 二进制文件,而无需调用 execve() 系统调用。 换句话说:您可以直接从内存中执行任意二进制文件,而无需将它们写入存储。 从反取证或红队的角度来看,这非常有用,使您能够更隐蔽地四处走动,同时仍将编译的二进制文件放在目标机器上。 该工具适用于受支持的 Linux 平台 x86x86-64aarch64 )。 支持静态和动态编译的 ELF 二进制文件。 memfd_create() 当然,总会有一小部分二进制文件可能无法工作或导致崩溃,因此在现代系统调用 之上实现了 100% 可靠的回退方法。

背景

Linux 用户态 execve 工具的历史可以追溯到大约 20 年。 grugqThe Design and Implementation of Userland Exec [1] 以及 Phrack 62 [2] 中的另一篇文章中 对此进行了第一次可靠的撰写。 直接从内存执行二进制文件的反取证技术是非常标准的。 例如, Rapid7 的 勇气 有一个名为的库 libreflect ,其中包含一个实用程序,该实用程序 noexec 也尝试仅通过反射执行 ELF。 然而,这个工具是用 C 语言编写的,它有一个隐含的要求,即您需要 noexec 在目标系统上传输二进制文件以及能够执行这个二进制文件。

在现代容器环境中,这绝对不再是可能的了。 然而,许多容器环境确实包含 Python 安装。 从反取证的角度来看,能够简单地通过或其他目标机器下载 Python 脚本 curl ,然后能够执行该脚本,然后悄悄地执行任意二进制文件,这是非常有用的。

这也是该工具全部在一个文件中实现的原因。 这应该更容易在目标系统上下载它,并且不必担心在能够运行它之前安装任何其他依赖项。 尽管此 Python 版本已被弃用,但该工具已使用 Python 2.7 进行测试。 有许多系统仍然有 2.x 版本,所以这很有用。

不存在 Python 用户态execve() 的其他良好实现 。 SELF [3]没有 被广泛记录,缺乏​​简单的调试选项,但更重要的是根本不起作用。 该 ulexecve 实现是从头开始编写的。 它解析 ELF 文件,加载和解析动态链接器(如果需要),将所有段映射到内存中,并最终构建一个包含 CPU 指令的跳转缓冲区,最终将控制从 Python 进程直接转移到新加载的二进制文件。

所有常见的 ELF 解析逻辑、设置堆栈、映射 ELF 段和设置跳转缓冲区都被抽象出来,因此很容易(大约几个小时)移植到另一个 CPU。 将其移植到其他基于 ELF 的平台(例如 BSD)可能会涉及更多一些,但应该仍然相当简单。 有关这样做的更多信息,只需检查代码中的注释。

请注意,没有外部依赖项并将所有内容都实现在单个源代码文件中是一个明确的设计目标。 如果您需要制作更小的有效负载,那么删除对某些 CPU 类型的支持或删除所有调试信息和其他选项应该是相当简单的。

安装

通过 pip 安装

尽管从反取证的角度来看这没有什么意义,但该工具可以通过 pip .

pip install ulexecve
ulexecve --help

作为 Python 包构建和安装

python setup.py sdist
python -m pip install --upgrade dist/ulexecve-<version>.tar.gz
ulexecve --help

通过 curl 下载和运行

curl -o ulexecve.py https://raw.githubusercontent.com/anvilsecure/ulexecve/docs/ulexecve.py
./ulexecve.py --help

用法

该工具完全支持静态和动态编译的可执行文件。 只需将二进制文件的文件名 ulexecve 和要提供给二进制文件的任何参数传递给即可。 该环境将直接从您执行的环境中复制过来 ulexecve

ulexecve /bin/ls -lha

stdin 如果您指定 - 为文件名, 您可以让它读取二进制文件。

cat /bin/ls | ulexecve - -lha

要将二进制文件下载到内存并立即执行,您可以使用 --download . 这会将文件名参数解释为 URI。

ulexecve --download http://host/binary

调试有几个选项可用。 如果您遇到崩溃,您可以通过显示调试信息 --debug 、通过建立的堆栈 --show-stack 以及生成的跳转缓冲区 --show-jumpbuf--jump-delay 如果您想正确解析和映射 ELF,然后附加调试器以单步执行跳转缓冲区和最终执行的二进制文件以查找崩溃原因, 该选项非常有用。

cat /bin/echo | ulexecve --debug --show-stack --show-jumpbuf - hello
...
PT_LOAD at offset 0x0002c520: flags=0x6, vaddr=0x2d520, filesz=0x1ad8, memsz=0x1c70
Loaded interpreter successfully
Stack allocated at: 0x7fddf630e000
vDSO loaded at 0x7ffd8952e000 (Auxv entry AT_SYSINFO_EHDR), AT_SYSINFO: 0x00000000
Auxv entries: HWCAP=0x00000002, HWCAP2=0x00000002, AT_CLKTCK=0x00000064
stack contents:
 argv
   00000000:   0x0000000000000002
   00000008:   0x00007fddf6312410
...
Generated mmap call (addr=0x00000000, length=0x00030000, prot=0x7, flags=0x22)
Generated memcpy call (dst=%r11 + 0x00000000, src=0x02534650, size=0x00000fc8)
Generated memcpy call (dst=%r11 + 0x0002d520, src=0x0253d720, size=0x00001ad8)
Generating jumpcode with entry_point=0x00001100 and stack=0x7fddf630e000
Jumpbuf with entry %r11+0x1100 and stack: 0x00007fddf630e000
Written jumpbuf to /tmp/tmphsiaygna.jumpbuf.bin (#592 bytes)
Executing: objdump -m i386:x86-64 -b binary -D /tmp/tmphsiaygna.jumpbuf.bin
...
245:   00 00 00
248:   4c 01 d9                add    %r11,%rcx
24b:   48 31 d2                xor    %rdx,%rdx
24e:   ff e1                   jmpq   *%rcx
...
Memmove(0x7fddf6f0e000, 0x0254d7f0, 0x00000250)
hello

总是有 --fallback 选择的。 它不像我们自己在用户空间中的二进制文件中解析和映射那样隐秘。 回退方法使用 memfd_create()fexecve() 但它应该 100% 的时间用于执行任意静态或动态二进制文件。 提供的二进制文件显然是您所在平台的正确二进制文件。

限制

显然,您总是会得到无法正确执行的二进制文件。 然而,这个实现非常干净且经过良好测试(它包括静态和动态二进制文件的单元测试、PIE 编译的可执行文件和具有不同运行时的可执行文件,例如 Rust 或 Go)。 对于上述平台上的大多数工具和二进制文件,它应该可以解决问题。 但是您的里程可能会有所不同。

移植

当移植到不同的平台时,确保少量的单元测试都能正常工作。 只需 ./test.py 在目标平台上运行包含的内容并修复所有内容,直到所有这些测试再次成功。

标签:工具分享, 主机安全, 二进制安全