slot2:一种两阶段 UEFI GRUB2 bootkit,可在 AWS EC2 UEFI x86_64 上的 Amazon Linux 2023 中持久化一个预操作系统阶段的网络植入程序。
作者 cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/slot2
slot2 是面向 EC2 上 Amazon Linux 2023 的 bootkit。AWS EC2 UEFI 固件会首先加载它。一个内嵌的植入程序在 bootkit UKI(Unified Kernel Image:一个包含 bootkit 内核和 initrd 的 EFI 文件)中、在原生操作系统启动之前运行,网络也已就绪,随后使用 kexec 启动正常启动本会使用的原生内核。最终启动起来的操作系统是真实的、原生的系统。
持久化通过一个 UEFI NVRAM 加载选项和两个文件实现:一个 bootkit ESP 文件和一个 bootkit UKI;不使用也不需要任何用户态辅助程序/rootkit 服务,也不是替换厂商引导加载程序。在 slot2 中,98-payload.sh 是用于预启动 C2/渗透/外传以及任意磁盘写入的植入程序,可向目标文件系统插入 RAT 或基于内核的 rootkit 等载荷。本代码树中的植入程序是一个演示:它写入 /root/HELLO.TXT 并启动网络以建立互联网/网络连接,在实际行动中这可以是网络渗透或外传。
UEFI 固件本身不会挑选内核。它会遍历 NVRAM 加载选项的 BootOrder,每个选项都是一个编号槽位(Boot0001、Boot0002、……),指向 ESP 上的一个 EFI 可执行文件。在 EC2 上,Boot0001 始终存在:即启动原生 \EFI\BOOT\BOOTX64.EFI 的 Amazon EBS 条目。slot2 添加 Boot0002,指向同一 ESP 上的第二个文件,并将 Boot0002 置于 BootOrder 首位。固件仍以 Boot0001 作为回退。
第二个文件必须是固件能够执行的内容,并且必须能够到达根文件系统上的 bootkit UKI,而固件无法看到该文件系统。之所以使用 GRUB,是因为它可以读取 GPT 和 XFS、找到 UKI 并链式加载它。该镜像是自定义的 grub2-mkimage,而非打包的 Amazon GRUB;原生 BOOTX64.EFI 仍保留在厂商放置的位置。GRUB 在此的唯一职责就是移交给 UKI。
随后 UKI 内核运行 98-payload.sh,然后 99-kexec-stock.sh 使用 kexec 启动正常 BLS 启动本会使用的原生内核和 initrd。
EC2 UEFI NVRAM
├── Boot0001 -> stock \EFI\BOOT\BOOTX64.EFI (untouched)
└── Boot0002 -> bootkit \EFI\BOOT\.BOOTX64.EFI (created; first on BootOrder)
|
v
ESP (VFAT) /boot/efi
└── /EFI/BOOT/
├── BOOTX64.EFI stock (untouched) <- Boot0001
└── .BOOTX64.EFI bootkit GRUB2 PE --chainload--+ <- Boot0002
|
root FS (XFS, typical AL2023) |
└── /var/lib/systemd/boot/ |
└── uki.efi <------------------------------------------+
|
| 98-payload.sh, then 99-kexec-stock.sh
| resolve stock target under /sysroot/boot/:
| loader/entries/*.conf (BLS + grubenv; AL2023 primary)
| grub2/grub.cfg (legacy fallback)
|
+-- kexec ----------------------------------------+
|
/boot (stock, untouched) |
├── vmlinuz-* <--------------------------------------------+
└── initramfs-*.img
|
+-- stock initrd -> stage-2 stock userspace
如果 kexec 失败,阶段 1 会继续在 UKI 内核上运行。机器仍可启动。
植入体是 98-payload.sh,它是 bootkit UKI 内的一个 dracut pre-pivot 钩子。它不是启动后的用户空间服务。此代码树中的实现是一个演示:以读写方式重新挂载 /sysroot,写入 /root/HELLO.TXT,启用网卡,进行 DHCP,然后 wget ifconfig.me(5 秒预算)。DHCP 租约和解析器文件保留在 initrd 中(/tmp/udhcpc.*.lease、/etc/resolv.conf);它们不会被写入真实根文件系统。网络故障不会阻止 kexec。
阶段 1 将真实根文件系统挂载在 /sysroot(通常是 XFS,以读写方式重新挂载)。/boot 是 /sysroot/boot。额外的 EBS 卷不会被挂载,除非钩子挂载它们。精简 initrd 附带 busybox 的 udhcpc 和 wget。ip(8) 是真实根文件系统中的二进制文件,通过 initrd 加载器运行。
运行时主机是目标(运行 deploy 然后重启进入 bootkit 的 EC2 实例)。
uefi 或 uefi-preferred 的 EC2 实例/boot/efi(systemd automount 也可以;deploy 会触发它)libefivar 和 libefiboot(deploy 是动态链接的;两者都随原版操作系统提供)ARM64 / Graviton 未测试,因此不支持(暂时?)。
Amazon Linux 2023 x86_64。
./install-dependencies.sh 需要 Root 权限/boot/vmlinuz-$(uname -r) 和 /boot/initramfs-$(uname -r).img(这些文件会被打包进 UKI)cc、make、pkg-config、grub2-mkimage、objcopy、kexec、openssl、xxd./install-dependencies.sh 是引导脚本。它安装上述 dnf 包(以及 grub2-efi-x64-modules、systemd-boot-unsigned、efivar-devel、dracut),并获取固定版本的 busybox udhcpc 和 wget 二进制文件。
在构建主机上:
./install-dependencies.sh
make
直接运行 make 会生成 uki.efi、BOOTX64.EFI 和 deploy。它不会写入 ESP、/var/lib/systemd/boot 或 NVRAM。尾部魔数和 SOURCE_DATE_EPOCH 详见 ARCHITECTURE.md。
将随附的 deploy ELF 复制到目标机上。无需编译器。uki.efi 和 BOOTX64.EFI 已包含在尾部数据中。
scp deploy user@target-host:~/
在共置的 AL2023 实例上,复制是可选的;从构建树中运行 ./deploy。
在下次重启时,固件 Boot0002 加载 ESP PE,PE 链式加载 UKI,payload 运行,然后 kexec 启动原版 BLS 内核(ARCHITECTURE.md)。如果 deploy 打印 EFI variables are not supported on this system,则本次启动为 BIOS;请参阅 Requirements。
deploy 需要 root 权限。不带参数时,它会转储 trailer 偏移量和 NVRAM,不写入任何内容。-y 会植入镜像和 NVRAM,然后擦除并取消链接此可执行文件。任何其他参数都会打印用法。
./deploy # dump only: trailer offsets and NVRAM; no writes
./deploy -y # plant, then wipe and unlink this binary
reboot
在成功执行 -y 运行后,该进程会在自身退出后安排擦除其自身的可执行文件(/dev/urandom、截断、删除),因此 ls deploy 应当失败。植入的 UKI 和 bootkit GRUB 镜像会保留。
-y 会写入 /var/lib/systemd/boot/uki.efi(root:root,0500)和 /boot/efi/EFI/BOOT/.BOOTX64.EFI(root:root,在 VFAT 上尽力设置为 0700),然后创建 NVRAM Boot0002 并将其置于 BootOrder 首位。原版 BOOTX64.EFI 仍为 Boot0001。
make
这会构建三个产品。没有树内测试套件。在稍后将运行 ./deploy -y 的 AL2023 实例上就地执行 make 并不能证明运行时是干净的。
重启后,你应处于 stage-2 原版用户空间:即正常 BLS 启动本会使用的内核、/proc/cmdline 和 initrd 路径。
cat /root/HELLO.TXT
# expect: stage-1 cmdline with BOOTKIT_MARKER, 98-payload.sh banner,
# pre-OS network breadcrumbs (NIC up, DHCP/udhcpc, default route),
# wget / external_ip=... / result, then
# 99-kexec-stock: resolve=bls:..., kexec -l ok, kexec -e
grep BOOTKIT_MARKER /proc/cmdline || echo "no marker (stage-2 ok)"
# expect: no marker
dmesg | head -3
# expect: stock-style cmdline (BLS options), not BOOTKIT_MARKER=1
uname -r
# expect: the dnf/grubby default on disk
deploy -y 会写入以下默认文件。NVRAM 中的 BootOrder 和 Boot0002 属于固件状态,而非文件。
| 路径 | 作用 |
|---|---|
/var/lib/systemd/boot/uki.efi | 第一阶段 UKI(内核、bootkit initrd、cmdline) |
/boot/efi/EFI/BOOT/.BOOTX64.EFI | Bootkit GRUB2 PE;固件 Boot0002 加载此文件 |
/boot/efi/EFI/BOOT/BOOTX64.EFI | 原始 PE(未改动);Boot0001 回退项 |
/boot/vmlinuz-*、/boot/initramfs-*.img、/boot/loader/entries/ | 原始内核、initrd 和 BLS;即 kexec 加载的内容 |
如果你更改了 ESP PE 名称或 UKI 路径,请保持 mk-bootx64-efi.sh、deploy.c 和 NVRAM 条目同步(参见 ARCHITECTURE.md)。
amzn / 2023)和 EC2 形态的 Boot0001 为门槛条件。内嵌 PE 检查要求 AMD64(0x8664)。ARM64 / Graviton 目前(尚?)不支持。open 调用不在本项目范围内。deploy 的自擦除是尽力而为的。它不是 SSD 或 NVMe 上的安全擦除。建议使用自定义 deploy 以实现完整武器化和更高程度的 OPSEC。c5a.xlarge 和 t3.nano 上开发和测试。