
NVRAM 부팅 옵션을 통해 부팅 전 네트워크 임플란트를 설치하고, UKI를 체인로드하며, dracut 페이로드를 실행한 뒤 스톡 커널로 kexec하는 UEFI GRUB2 부트킷
slot2: AWS EC2 UEFI x86_64의 Amazon Linux 2023에서 OS 부팅 전 네트워크 임플란트를 지속시키는 2단계 UEFI GRUB2 부트킷입니다.
by cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/slot2
slot2는 EC2의 Amazon Linux 2023을 위한 부트킷입니다. AWS EC2 UEFI 펌웨어가 이를 가장 먼저 로드합니다. 내장된 임플란트는 부트킷 UKI(Unified Kernel Image: 부트킷 커널과 initrd가 포함된 단일 EFI 파일)에서 네트워크를 포함한 상태로 기존 OS가 부팅되기 전에 실행되며, 이후 kexec를 사용하여 정상 부팅에서 사용되었을 기존 커널을 시작합니다. 부팅되는 운영 체제는 실제 기존 OS입니다.
지속성은 UEFI NVRAM 로드 옵션과 두 개의 파일, 즉 부트킷 ESP 파일과 부트킷 UKI를 통해 이루어집니다. 사용자 공간 헬퍼/루트킷 서비스는 사용되지 않으며 필요하지도 않고, 교체된 벤더 부트로더도 아닙니다. slot2에서 98-payload.sh는 부팅 전 C2/유출/반입 및 대상의 파일 시스템에 RAT나 커널 기반 루트킷과 같은 페이로드를 삽입하기 위한 임의 디스크 쓰기에 사용되는 임플란트입니다. 이 트리의 임플란트는 시연용입니다. /root/HELLO.TXT를 작성하고 네트워크를 활성화하여 인터넷/네트워크 연결을 설정하는데, 실제 작전에서는 네트워크 반입 또는 유출이 될 수 있습니다.
UEFI 펌웨어는 스스로 커널을 선택하지 않습니다. NVRAM 로드 옵션의 BootOrder를 순회하며, 각각은 번호가 매겨진 슬롯(Boot0001, Boot0002, ...)으로 ESP의 하나의 EFI 실행 파일을 가리킵니다. EC2에서 Boot0001은 항상 존재합니다. 기존 \EFI\BOOT\BOOTX64.EFI를 시작하는 Amazon EBS 항목입니다. slot2는 Boot0002를 추가하여 동일한 ESP의 두 번째 파일을 가리키게 하고, Boot0002를 BootOrder의 첫 번째로 둡니다. 펌웨어는 여전히 Boot0001을 폴백으로 가집니다.
그 두 번째 파일은 펌웨어가 실행할 수 있는 것이어야 하며, 펌웨어가 볼 수 없는 루트 파일 시스템의 부트킷 UKI에 도달해야 합니다. GRUB이 사용되는 이유는 GPT와 XFS를 읽고, UKI를 찾고, 이를 체인로드할 수 있기 때문입니다. 이미지는 패키지된 Amazon GRUB이 아닌 사용자 정의 grub2-mkimage이며, 기존 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가 실패하면 stage 1은 UKI 커널에서 계속 진행된다. 박스는 부팅 가능한 상태로 유지된다.
임플란트는 98-payload.sh로, bootkit UKI 내부의 dracut pre-pivot 훅이다. 부팅 후의 userspace 서비스가 아니다. 이 트리의 구현은 데모이다: /sysroot를 읽기-쓰기로 다시 마운트하고, /root/HELLO.TXT를 쓰고, NIC를 활성화하고, DHCP를 수행한 뒤, ifconfig.me를 wget한다(5초 예산). DHCP 임대와 리졸버 파일은 initrd(/tmp/udhcpc.*.lease, /etc/resolv.conf)에 남아 있으며, 실제 루트에 기록되지 않는다. 네트워크 실패는 kexec를 차단하지 않는다.
Stage 1은 실제 루트를 /sysroot에 두고 있다(일반적으로 XFS, rw로 다시 마운트됨). /boot는 /sysroot/boot이다. 훅이 마운트하지 않는 한 추가 EBS 볼륨은 마운트되지 않는다. 슬림 initrd에는 busybox udhcpc와 wget이 포함되어 있다. ip(8)은 실제 루트의 바이너리로, initrd 로더를 통해 실행된다.
런타임 호스트는 대상(deploy를 실행한 뒤 bootkit으로 재부팅하는 EC2 인스턴스)이다.
uefi 또는 uefi-preferred인 UEFI를 지원하는 EC2 인스턴스/boot/efi에 마운트된 ESP(systemd automount도 무방하며, deploy가 이를 트리거함)libefivar 및 libefiboot(deploy는 동적 링크되며, 둘 다 기본 OS에 포함됨)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를 체인로드하며, 페이로드가 실행된 후 kexec가 기본 BLS 커널을 시작합니다(ARCHITECTURE.md). deploy가 EFI variables are not supported on this system을 출력하면 이번 부팅은 BIOS 방식이므로, 요구 사항을 참조하십시오.
deploy는 root 권한이 필요합니다. 인수 없이 실행하면 트레일러 오프셋과 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, truncate, unlink)의 삭제를 예약하므로 ls deploy는 실패해야 합니다. 심어둔 UKI와 부트킷 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를 실행하는 것은 깨끗한 런타임 증명이 아닙니다.
재부팅 후에는 일반적인 BLS 부팅이 사용했을 커널, /proc/cmdline, initrd 경로를 갖춘 stage-2 기본 사용자 공간에 있어야 합니다.
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 | 1단계 UKI (커널, 부트킷 initrd, cmdline) |
/boot/efi/EFI/BOOT/.BOOTX64.EFI | 부트킷 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에서의 안전한 삭제가 아닙니다. 완전한 무기화와 더 높은 수준의 OPSEC을 위해서는 사용자 정의 deploy를 권장합니다.c5a.xlarge와 t3.nano에서 개발 및 테스트되었습니다.