_._     _,-'""`-._
(,-.`._,'(       |\`-/|
    `-.-' \ )-`( , o o)
          `-    \`_`"'-

copy fail 2 v6: the IPv6 dual the v4 patch missed

2026-05-07 · linuxkernelxfrmesp6ipv6msg_splice_pagespage-cachecopyfaillpeexploit-dev

i'd just finished the v4 chain for Copy Fail 2 when i went looking for the v6 dual. the fix at f4c50a4034 only touched net/ipv4/esp4.c. the v6 copy at net/ipv6/esp6.c:913-924 is the same code, line for line:

// net/ipv6/esp6.c: esp6_input, no-COW fast path (unchanged)
if (!skb_cloned(skb)) {
    if (!skb_is_nonlinear(skb)) {
        nfrags = 1;
        goto skip_cow;
    } else if (!skb_has_frag_list(skb)) {
        nfrags = skb_shinfo(skb)->nr_frags;
        nfrags++;
        goto skip_cow;
    }
}

err = skb_cow_data(skb, 0, &trailer);
...

skip_cow:
    ...
    err = skb_to_sgvec(skb, sg, 0, skb->len);
    ...
    aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
    err = crypto_aead_decrypt(req);

the producer side carries the same omission. __ip6_append_data at net/ipv6/ip6_output.c:1786-1797 calls skb_splice_from_iter and never sets SKBFL_SHARED_FRAG. it mirrors the v4 root cause exactly.

the v4 fix only lives in v4. so every distro that pulled f4c50a4034 still ships an exploitable v6 path. verified it live on stock Ubuntu 26.04 LTS, kernel 7.0.0-15.

the one thing v6 does differently

forking the v4 PoC to AF_INET6 + ::1 + ip -6 xfrm is a one-liner per location, mostly. the snag is a single v6-only gate that drops short packets before they ever reach esp6_input:

// net/ipv6/xfrm6_input.c:124
if (skb->len < ip6hlen + len) {
    /* packet is too small!?! */
    return -EINVAL;
}

with ip6hlen = 40 and len = sizeof(udphdr) = 8, the UDP payload has to be at least 40 bytes. the Copy Fail 2 v4 packet is 33 bytes (8 ESP header + 8 IV + 1 target byte + 16 ICV), so v4 sails right through. the v6 PoC pads 16 attacker-file bytes between the target byte and the ICV to clear the gate. v4 has no equivalent check; its threshold is 28.

chain

same as v4. aa-rootns harness, esp6 / xfrm_user / xfrm_algo autoload, byte-flip a nologin line in /etc/passwd into sick::0:0:<pad>:/:/bin/bash, su - sick through PAM nullok. the repo ships a v6 run.sh next to the v4 one.

repo

codeberg.org/sickkk/Copy_Fail2-Electric_Boogaloo . the v6 PoC is in ipv6/: ipv6/copyfail2v6.c, ipv6/run.sh.

unprivileged userns over ::1 loopback. no IPv6 routing required.

credits

. _SiCk · afflicted.sh