Environment
- OS: Ubuntu 24.04 (ARM64)
- Kernel:
6.8.0-106-generic
- Hardware: QEMU Virtual Machine
Summary
To run the exploit on ARM64, I replaced the tiny shellcode with an ARM64 equivalent and adjusted the payload length accordingly. The exploit then fails with a kernel oops at the rxrpc stage.
Reproduction
- Replace the embedded x86_64 tiny shellcode with an ARM64 equivalent and adjust the payload-length constant.
- Run
./exploit -v as a regular user (uid=1000).
Expected
A root shell.
Observed
- During the
/usr/bin/su overwrite step, [su] post-write verify failed (target unchanged) is printed — the write does not stick.
- During the rxrpc spray, the kernel oopses at
flush_dcache_page+0x18/0x58.
- Call path:
flush_dcache_page ← skcipher_walk_done ← crypto_pcbc_encrypt [pcbc] ← rxkad_secure_packet [rxrpc] ← rxrpc_send_data ← rxrpc_sendmsg ← sendmsg(2).
- The faulting address
fffffe00021538c8 is in the ARM64 vmemmap region, suggesting a corrupted struct page pointer reaches flush_dcache_page (pgd=0, level-0 translation fault).
Why I think the shellcode itself isn’t the problem
With AppArmor disabled, the same build produces a root shell as expected. That suggests the replaced ARM64 shellcode itself is correct, and the failure occurs in the rxrpc spray / SU overwrite path under ARM64 + AppArmor enabled. Whether this is an rxrpc bug, or whether the ARM64 + AppArmor combination causes the spray to land on the wrong page, I cannot say definitively.
Code modifications I made for the ARM64 port
For reference, here are the diffs I applied to exp.c. None of them touch the rxrpc / kmalloc-spray logic itself.
1. Payload structure and size (x86_64 → ARM64 Tiny ELF)
- Original: x86_64 shellcode targeting
/usr/bin/su combined with a minimal ELF header.
- Modified: The
shell_elf array now contains ARM64 root shellcode combined with an ARM64 Tiny ELF header crafted to satisfy the kernel loader. Because ARM64 uses fixed 4-byte instructions, the payload is larger, so PAYLOAD_LEN was bumped to 240 bytes.
2. File-overwrite verification logic (inside su_lpe_main)
- Original: After overwriting the page cache, the code reads the byte at offset
0x78 (entry point) and checks it against the first byte of the x86_64 shellcode.
- Modified: The verification bytes were changed to the ARM64 opcode bytes
0xe0 and 0x03.
/* [modified] ARM64 entry-point opcode verification (0xe0, 0x03) */
if (verify_byte(TARGET_PATH, ENTRY_OFFSET, 0xe0) != 0 ||
verify_byte(TARGET_PATH, ENTRY_OFFSET + 1, 0x03) != 0) {
3. “Already patched” marker check
- Original: Before running the exploit, the
su_marker array (used to detect whether the system has already been compromised) held x86_64 instruction bytes.
- Modified: The marker array was replaced wholesale so that it matches the first 8 bytes of the injected ARM64 shellcode.
Full log
ubuntu@poc-vm:~$ ./exploit -v
[su] installed 60 xfrm SAs
[su] wrote 240 bytes to /usr/bin/su starting at 0x0
[su] post-write verify failed (target unchanged)
=== rxrpc/rxkad LPE EXPLOIT (uid=1000 → root) ===
[+] K_A found after 37782 iters in 0.01s
[+] K_B found after 381323 iters in 0.07s
[+] K_C found after 26935928 iters in 4.85s
[ 6625.068388] Unable to handle kernel paging request at virtual address fffffe00021538c8
[ 6625.069626] Mem abort info:
[ 6625.070239] ESR = 0x0000000096000004
[ 6625.070455] EC = 0x25: DABT (current EL), IL = 32 bits
[ 6625.070823] SET = 0, FnV = 0
[ 6625.071212] EA = 0, S1PTW = 0
[ 6625.071336] FSC = 0x04: level 0 translation fault
[ 6625.071594] Data abort info:
[ 6625.071749] ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000
[ 6625.072229] CM = 0, WnR = 0, TnD = 0, TagAccess = 0
[ 6625.072691] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[ 6625.073198] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000131b1d000
[ 6625.074379] [fffffe00021538c8] pgd=0000000000000000, p4d=0000000000000000
[ 6625.075427] Internal error: Oops: 0000000096000004 [#2] SMP
[ 6625.076666] Modules linked in: fcrypt pcbc rxrpc ip6_udp_tunnel udp_tunnel authencesn authenc echainiv esp4 xfrm_user xfrm_algo algif_skcipher af_alg tls isofs binfmt_misc nls_iso8859_1 sch_fq_codel dm_multipath efi_pstore nfnetlink dmi_sysfs ip_tables x_tables autofs4 btrfs blake2b_generic raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor xor_neon raid6_pq libcrc32c raid1 raid0 crct10dif_ce polyval_ce polyval_generic ghash_ce sm4 sha3_ce sha2_ce sha256_arm64 sha1_ce aes_neon_bs aes_neon_blk aes_ce_blk aes_ce_cipher
[ 6625.088058] CPU: 1 PID: 1850 Comm: exploit Tainted: G D 6.8.0-106-generic #106-Ubuntu
[ 6625.090266] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024
[ 6625.092876] pstate: 61400005 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
[ 6625.094826] pc : flush_dcache_page+0x18/0x58
[ 6625.095579] lr : skcipher_walk_done+0x188/0x308
[ 6625.096262] sp : ffff8000854e34d0
[ 6625.096746] x29: ffff8000854e34d0 x28: ffff0000c8629800 x27: 0000000000000008
[ 6625.097795] x26: ffff0000c86298c0 x25: ffff0000c60a60c0 x24: ffff0000c53fa880
[ 6625.098905] x23: 0000000000000008 x22: 0000000000000008 x21: 0000000000000000
[ 6625.100073] x20: 0000000000000000 x19: ffff8000854e3510 x18: ffff800083a7b090
[ 6625.101130] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 6625.102231] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000
[ 6625.103516] x11: 0000000000000000 x10: 0000000000000000 x9 : ffff80008082b090
[ 6625.104785] x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000
[ 6625.106065] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000
[ 6625.107230] x2 : 0000000000000000 x1 : 0000000000000670 x0 : fffffe00021538c0
[ 6625.108289] Call trace:
[ 6625.108640] flush_dcache_page+0x18/0x58
[ 6625.109302] skcipher_walk_done+0x188/0x308
[ 6625.109888] crypto_pcbc_encrypt+0xe8/0x168 [pcbc]
[ 6625.110754] crypto_skcipher_encrypt+0x12c/0x198
[ 6625.111519] rxkad_secure_packet+0x104/0x2b8 [rxrpc]
[ 6625.112388] rxrpc_send_data+0x23c/0x658 [rxrpc]
[ 6625.112881] rxrpc_do_sendmsg+0x1d8/0x398 [rxrpc]
[ 6625.113496] rxrpc_sendmsg+0x134/0x1a8 [rxrpc]
[ 6625.114098] __sock_sendmsg+0x80/0x108
[ 6625.114720] ____sys_sendmsg+0x28c/0x348
[ 6625.115266] ___sys_sendmsg+0xbc/0x140
[ 6625.115813] __sys_sendmsg+0x94/0x120
[ 6625.116429] __arm64_sys_sendmsg+0x30/0x60
[ 6625.116997] invoke_syscall+0x7c/0x130
[ 6625.117579] el0_svc_common.constprop.0+0x4c/0x140
[ 6625.118461] do_el0_svc+0x28/0x58
[ 6625.119084] el0_svc+0x40/0x1c0
[ 6625.119560] el0t_64_sync_handler+0x148/0x158
[ 6625.120403] el0t_64_sync+0x1b0/0x1b8
[ 6625.120877] Code: d503233f f800865e a9bf7bfd 910003fd (f9400401)
[ 6625.121532] ---[ end trace 0000000000000000 ]---
Segmentation fault
Questions
Is ARM64 within the officially intended scope of this PoC, or is it x86_64-only? If specific debug data would help (KASAN build output, particular sysctls, etc.), let me know and I can rerun and attach it.
Environment
6.8.0-106-genericSummary
To run the exploit on ARM64, I replaced the tiny shellcode with an ARM64 equivalent and adjusted the payload length accordingly. The exploit then fails with a kernel oops at the rxrpc stage.
Reproduction
./exploit -vas a regular user (uid=1000).Expected
A root shell.
Observed
/usr/bin/suoverwrite step,[su] post-write verify failed (target unchanged)is printed — the write does not stick.flush_dcache_page+0x18/0x58.flush_dcache_page←skcipher_walk_done←crypto_pcbc_encrypt [pcbc]←rxkad_secure_packet [rxrpc]←rxrpc_send_data←rxrpc_sendmsg←sendmsg(2).fffffe00021538c8is in the ARM64 vmemmap region, suggesting a corruptedstruct pagepointer reachesflush_dcache_page(pgd=0, level-0 translation fault).Why I think the shellcode itself isn’t the problem
With AppArmor disabled, the same build produces a root shell as expected. That suggests the replaced ARM64 shellcode itself is correct, and the failure occurs in the rxrpc spray / SU overwrite path under ARM64 + AppArmor enabled. Whether this is an rxrpc bug, or whether the ARM64 + AppArmor combination causes the spray to land on the wrong page, I cannot say definitively.
Code modifications I made for the ARM64 port
For reference, here are the diffs I applied to
exp.c. None of them touch the rxrpc / kmalloc-spray logic itself.1. Payload structure and size (x86_64 → ARM64 Tiny ELF)
/usr/bin/sucombined with a minimal ELF header.shell_elfarray now contains ARM64 root shellcode combined with an ARM64 Tiny ELF header crafted to satisfy the kernel loader. Because ARM64 uses fixed 4-byte instructions, the payload is larger, soPAYLOAD_LENwas bumped to 240 bytes.2. File-overwrite verification logic (inside
su_lpe_main)0x78(entry point) and checks it against the first byte of the x86_64 shellcode.0xe0and0x03.3. “Already patched” marker check
su_markerarray (used to detect whether the system has already been compromised) held x86_64 instruction bytes.Full log
Questions
Is ARM64 within the officially intended scope of this PoC, or is it x86_64-only? If specific debug data would help (KASAN build output, particular sysctls, etc.), let me know and I can rerun and attach it.