Sploitus

Exploit for CVE-2026-74586

githubexploit Β· 2026-09-09

Exploit Code

README148 lines
## https://sploitus.com/exploit?id=81D47119-80BF-5E81-B7D4-894970186697
# CVE-2026-74586 β€” SCTP ASCONF new_transport use-after-free trigger

A self-contained, unprivileged PoC for CVE-2026-74586, a use-after-free in
the Linux kernel's SCTP ASCONF (dynamic address reconfiguration) handling.
Credit for the bug itself goes to Qing Ming, who reported it upstream; the
fix is commit beb33f8ee1ca ("sctp: clear new_transport when removing a
peer"), merged 2026-08-12 with `Cc: stable` β€” Red Hat rates it important,
third-party trackers put it at CVSS 9.8.

What this repo adds on top of the advisory:

- a working raw-packet trigger, no kernel patches or special tools needed
- measured reliability on a stock distro kernel: **1,394 hits in 1,397
  automated runs (99.8%)** against Kali's 7.1.5+kali-amd64
- the three injection barriers that have to be solved for any raw ASCONF
  work, since the kernel silently drops the packet otherwise
- struct offsets for the 7.1.5 sctp module (from objdump, not guesswork)
  and notes on how far weaponization gets before it hits a hard wall

## The bug

`sctp_process_asconf_param()` stores the transport created by an ADD-IP
parameter in `asoc->new_transport`. A DEL-IP for the same address in the
same ASCONF chunk frees that transport through `sctp_assoc_rm_peer()`,
which (before the fix) does not clear `new_transport`. When
`sctp_process_asconf()` returns, `sctp_sf_do_asconf()` acts on the stale
pointer β€” on a stock kernel the visible effect is a HEARTBEAT emitted
toward the address of the just-freed transport; under KASAN it's a
slab-use-after-free report.

One ASCONF chunk is enough:

```
[Address Parameter L] [ADD-IP G] [DEL-IP G]
```

## Building and running

```
gcc -O2 -Wall -o poc poc.c
./poc
```

No root. The PoC unshares a user + network namespace, sets the SCTP
sysctls in it, brings up a multi-homed association over loopback, then
injects the crafted ASCONF with a raw socket.

Expected output on a vulnerable kernel:

```
[+] Association established (no AUTH)
[+] server_vtag=0x... captured_tsn=0x...
[*] serial=0x... (= initial_tsn)
[+] Injected 60 bytes
  [9222->9111 vt=...] ASCONF-ACK(0x80) len=8
  [+] ASCONF-ACK -> server processed ADD-IP + DEL-IP
  [9222->9111 vt=...] HB(0x04) len=60        9222 vt=...] ABORT(0x06) len=8
[+] GhostTransport UAF TRIGGERED (ASCONF-ACK received)
[+] HEARTBEAT to ghost address observed (dangling transport USED)
```

The HEARTBEAT after the ACK is the interesting part: that packet only
exists because the kernel walked the dangling `new_transport`. The client
ABORTs right after because the heartbeat came back out-of-the-blue.

## Why raw injection is annoying (the three barriers)

None of this is novel research, it's just undocumented enough to cost an
afternoon, so here it is for the next person:

1. **CRC32c.** Loopback does not hand you CHECKSUM_UNNECESSARY for raw
   socket packets, so `sctp_rcv()` verifies the checksum and drops on
   mismatch. Compute CRC32C (poly 0x82F63B78, reflected) over the whole
   SCTP packet with the checksum field zeroed.
2. **The second auth check.** `sctp_auth_recv_cid()` runs in the input
   path *before* the state machine and drops unauthenticated ASCONF even
   when `addip_noauth_enable=1` β€” that sysctl only relaxes the check
   inside `sctp_sf_do_asconf()`. The way out is to never negotiate AUTH
   at the socket level in the first place (no `SCTP_AUTH_SUPPORTED`
   setsockopt), so `peer.auth_capable` stays false and both checks pass.
3. **The serial.** First accepted ASCONF serial = peer's initial TSN.
   Capture any DATA chunk, take its TSN, subtract one. Off-by-one here
   and the chunk is silently discarded.

## Affected / fixed

The `Fixes:` tag on the upstream commit is 6af29ccc223b ("sctp: Bundle
HEARTBEAT into ASCONF_ACK"), so the code is old. Fixed in mainline and
backported to the stable series (Debian tracker: fixed from 7.1.9-1 in
sid, 6.12.107 in trixie-security). Kali rolling as of 2026-09-09 ships
7.1.5-1kali1 in every suite, which predates all of it β€” that's the main
practical relevance of this PoC.

## Weaponization notes (where this stops)

For anyone taking it further β€” measured against 7.1.5 in QEMU, offsets
from `objdump -d` of the shipped `sctp.ko`:

| field | offset |
|---|---|
| flowi | 0x30 |
| ipaddr | 0x68 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |

- The free is single (RCU callback `sctp_transport_destroy_rcu`), there
  is no double-free on this path. If you were hoping to close a socket
  and get two frees β€” `sctp_association_free()` only walks the transport
  list, and `rm_peer()` already unlinked the ghost. Don't spend an
  evening on that.
- `sctp_outq_select_transport()` reads `state` at +0x15c off
  `chunk->transport` at +0xe8 of the chunk. A reclaimed object sprayed
  with `state=1` (ACTIVE) changes kernel behavior after the free β€” the
  object is being consulted.
- The gate past that is the empty-list check on +0x288: the kernel
  compares `*(p+0x288)` against `p+0x288`. That is a self-pointer. You
  cannot spray your way past it without knowing the slab address, i.e.
  this bug needs an info leak before `af_specific` control turns into
  instruction-pointer control. `af_specific` itself is an indirect call
  site (`*(af_specific+0x18)` with rdi = transport), so that's the prize
  once a leak exists.
- Spray primitive that works: `setsockopt(SCTP_AUTH_KEY)` with an 800-byte
  key lands in the same kmalloc-1k cache. Remember the header is
  `struct sctp_authkey { assoc_id; keynumber; keylength; key[] }` and
  AUTH needs enabling per-socket first via `SCTP_AUTH_SUPPORTED` with an
  `sctp_assoc_value` β€” a plain int gets EINVAL.

## Scope

This is a trigger/crash PoC. It is not a privilege escalation and this
repo does not claim one. Run it against your own kernels in a VM.

Tested on: 7.1.5+kali-amd64 (Kali rolling) and 7.0.12+kali-amd64.

## References

- Fix: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=beb33f8ee1ca83acddb2a5ae80f3d22ec550b4c3
- Debian tracker: https://security-tracker.debian.org/tracker/CVE-2026-74586
- Original report by Qing Ming (lore link in the commit message)

## License

GPL-2.0 for the kernel-derived portions of the logic; do whatever you
want with the rest. For authorized security testing and research only.