Vega VEGA / Bug List / CVE-2026-43501
Finding · Linux kernel · IPv6 RPL · Jul 04, 2026
CVE-2026-43501

Route of Death patch bypass: an intermediate-hop RPL wrap gives a remote OOB write and Linux LPE

net/ipv6/exthdrs.c · ipv6_rpl_srh_rcv
Affects Linux 5.7-rc1 - 7.1-rc1 Any distro with IPv6 + unprivileged namespaces
Disclosure timeline
  1. Reported
    Feb 23, 2026
  2. Fixed
    Apr 21, 2026
  3. Backported
    Jun 16, 2026
  4. Disclosed
    Jul 04, 2026

Summary

CVE-2026-43501 is a patch bypass of CVE-2023-2156 (“Route of Death”) in the IPv6 RPL source-routing receive path (ipv6_rpl_srh_rcv). The 2023 fix only closed the final-hop (segments_left == 0) case. The intermediate-hop case was left open, and it is the more interesting one: the same mac_header wrap fires, but instead of an skb_under panic we get a controllable 14-byte OOB write, reachable remotely with a single crafted IPv6 packet.

A short, misaligned OOB write does not sound like much, but with the right heap shaping it is enough to corrupt a TPACKET pg_vec pointer, alias a kernel .text page RW into userspace, and finish at a root shell. We used it to win $10,500 in Google’s kernelCTF.

Impact & exploitability

One crafted IPv6 packet lands a 14-byte, partially controlled OOB write at a fixed, misaligned page offset. The write itself is 100% remote; turning it into LPE may require runs from a local unprivileged process, since the pg_vec step needs CAP_NET_RAW.

  • Attacker model: a single crafted IPv6 packet; the full chain runs from a local unprivileged process inside a user + network namespace
  • Primitive: a 14-byte, partially controlled OOB write in an almost arbitrary kmalloc-x cache
  • Practical impact: splice a kernel .text address into a pg_vec pointer, then mmap the ring for a writable alias of that read-only code page
  • End result: arbitrary kernel read/write, kernel shellcode execution, and a root shell

Am I affected?

The receive path is compiled in whenever CONFIG_IPV6 is set; CONFIG_IPV6_RPL_LWTUNNEL only changes the default of the runtime flag. A target is vulnerable if:

  • it supports IPv6, and
  • its kernel is in the affected range 5.77.1 (May 2020 to May 2026), and
  • net.ipv6.conf.*.rpl_seg_enabled is 1 (remotely reachable), or unprivileged {user,net} namespaces are enabled (locally reachable, since a namespace hands you the CAP_NET_ADMIN needed to flip the flag).
Check whether a system is vulnerable to CVE-2026-43501

Root cause

pskb_expand_head() is called only when segments_left drops to zero, on the assumption that the recompressed SRH can only grow at the final hop. That assumption is wrong. On an intermediate hop, changing which segment is swapped into daddr changes the shared-prefix length with the last segment, which changes CmprE, which changes the recompressed size.

When the recompressed SRH is larger, the following skb_push() leaves less headroom than mac_len. skb_set_mac_header(skb, -skb->mac_len) stores a negative value into the 16-bit skb->mac_header, which wraps to a large offset, and skb_mac_header_rebuild() memmoves mac_len bytes to that wrapped offset, writing before head. With the right packet the write lands 14 bytes at page offset ff2, and its first 6 bytes (an L2 destination MAC we choose) are attacker-controlled.

Reproducer

The full exploit code can be found in our open source security research project, CyberMeowfia.

Exploit

The chain is short:

  • prefetch -> leak the KASLR base.
  • CVE-2026-43501 -> splice the high bytes of a kernel .text address into a TPACKET pg_vec pointer, keeping its low 16 bits.
  • pg_vec remap -> mmap() the corrupted ring so the aliased pointer maps that kernel .text page RW into userspace, then overwrite a syscall handler with our kernel shellcode.
  • kernel shellcode -> trigger the matching syscall, set core_pattern, and finish from userspace.

The full write-up covers the SRH growth math, the mac_header wrap, the heap shaping into pg_vec, and the kernel .text patching path.

Read the deep-dive

Patch

The upstream fix adds a headroom check, so the SRH is expanded whenever there is not enough room for the rebuilt MAC header, not only at the final hop:

if (unlikely(!hdr->segments_left)) {
if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3);
if (unlikely(!hdr->segments_left ||
skb_headroom(skb) < chdr_len + skb->mac_len)) {
if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0,

This closes the wrap. It does not preserve the functionality of a crafted packet; we proposed a larger fix that also keeps the metadata intact, but since RPL is barely used, the smaller version is the one that landed upstream. Fixed in Linux 7.1-rc1 and backported on Jun 16, 2026.

Mitigation

Update to a fixed kernel (the latest LTS). There is no site-level workaround: the trigger is a raw IPv6 packet handled inside the kernel, not anything a userspace service can filter. If you cannot patch yet, disabling unprivileged user namespaces removes the local path and keeping rpl_seg_enabled at 0 removes the remote one, but upgrading is the real fix.

Timeline & credit

Found by Nebula Security. We exploited kernelCTF with this bug on Feb 22, 2026 and reported it to security@kernel.org the next day. After a few rounds (the report was first rejected for needing namespaces to trigger), the bug was fixed on Apr 21, 2026 and backported on Jun 16. Google acknowledged the kernelCTF submission and rewarded us $10,500 on Jun 26; this summary and the full write-up went out on Jul 4.

Vega · AI security research

Find bugs in your code. Before anyone else does.

Sign up