
비특권 환경에서 실행되는 CVE-2026-74586 개념 증명(PoC)으로, Linux 커널 SCTP ASCONF use-after-free 취약점을 대상으로 합니다. 원시 패킷 트리거, 신뢰성 지표, 그리고 구조체 오프셋 및 스프레이 프리미티브를 포함한 상세한 무기화 분석을 제공합니다.
Linux 커널의 SCTP ASCONF(동적 주소 재구성) 처리에서 발생하는 use-after-free인 CVE-2026-74586에 대한 자체 완결형 비권한 PoC입니다. 버그 자체에 대한 공로는 이를 업스트림에 보고한 Qing Ming에게 있습니다. 수정은 커밋 beb33f8ee1ca("sctp: clear new_transport when removing a peer")이며, 2026-08-12에 Cc: stable과 함께 병합되었습니다. Red Hat은 이를 중요(important)로 평가하며, 제3자 트래커들은 CVSS 9.8로 책정합니다.
이 저장소가 권고문에 더해 제공하는 것:
sctp_process_asconf_param()은 ADD-IP 파라미터로 생성된 transport를 asoc->new_transport에 저장합니다. 같은 ASCONF 청크에서 같은 주소에 대한 DEL-IP가 있으면, sctp_assoc_rm_peer()를 통해 해당 transport가 해제되는데, 이 함수는 (수정 전에는) new_transport를 지우지 않습니다. sctp_process_asconf()가 반환되면 sctp_sf_do_asconf()가 오래된 포인터에 대해 동작합니다. 기본 커널에서 보이는 효과는 방금 해제된 transport의 주소를 향해 HEARTBEAT가 전송되는 것이며, KASAN에서는 slab-use-after-free 보고서가 나타납니다.
ASCONF 청크 하나면 충분합니다:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
루트가 필요 없습니다. PoC는 user + network 네임스페이스를 unshare하고, 그 안에서 SCTP sysctl을 설정하고, 루프백을 통해 멀티홈 연관을 설정한 다음, raw 소켓으로 조작된 ASCONF를 주입합니다.
취약한 커널에서의 예상 출력:
[+] 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 <- kernel using freed transport
[9111->9222 vt=...] ABORT(0x06) len=8
[+] GhostTransport UAF TRIGGERED (ASCONF-ACK received)
[+] HEARTBEAT to ghost address observed (dangling transport USED)
ACK 이후의 HEARTBEAT가 흥미로운 부분입니다. 이 패킷은 커널이 dangling 상태인 new_transport를 순회했기 때문에만 존재합니다. 클라이언트는 하트비트가 갑작스럽게 돌아왔기 때문에 바로 ABORT합니다.
이 중 어느 것도 새로운 연구는 아니며, 단지 오후 한나절을 소모할 만큼 문서화가 부족할 뿐이므로 다음 사람을 위해 여기에 남깁니다:
sctp_rcv()가 체크섬을 검증하고 불일치 시 드롭합니다. 체크섬 필드를 0으로 설정한 상태에서 전체 SCTP 패킷에 대해 CRC32C(다항식 0x82F63B78, 반사형)를 계산하십시오.sctp_auth_recv_cid()는 상태 머신 이전에 입력 경로에서 실행되며, addip_noauth_enable=1인 경우에도 인증되지 않은 ASCONF를 드롭합니다. 해당 sysctl은 sctp_sf_do_asconf() 내부의 검사만 완화합니다. 탈출 방법은 소켓 수준에서 AUTH를 아예 협상하지 않는 것(SCTP_AUTH_SUPPORTED setsockopt 없음)이며, 그러면 peer.auth_capable이 false로 유지되어 두 검사 모두 통과합니다.업스트림 커밋의 Fixes: 태그는 6af29ccc223b("sctp: Bundle HEARTBEAT into ASCONF_ACK")이므로 코드는 오래되었습니다. 메인라인에서 수정되었고 안정 시리즈로 백포트되었습니다(Debian 트래커: sid에서 7.1.9-1부터, trixie-security에서 6.12.107부터 수정됨). 2026-09-09 기준 Kali rolling은 모든 스위트에서 7.1.5-1kali1을 제공하며, 이는 위의 모든 수정보다 앞선 버전입니다 — 이것이 이 PoC의 주요 실질적 관련성입니다.
더 나아가려는 사람을 위해 — QEMU의 7.1.5를 기준으로 측정했으며, 오프셋은 배포된 sctp.ko의 objdump -d에서 가져왔습니다:
| 필드 | 오프셋 |
|---|---|
| flowi (88 바이트) | 0x30 |
| ipaddr | 0x88 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |
sctp_association->new_transport는 0x6b0에 위치합니다. 이 표는 실행 중인 커널에서 pahole -C sctp_transport /sys/kernel/btf/vmlinux로 얻은 것입니다 — 이 표의 이전 개정판에는 잘못된 ipaddr 오프셋(0x68, flowi 계산 오류에서 비롯됨)이 있었습니다. BTF가 진실의 기준입니다.
sctp_transport_destroy_rcu). 이 경로에서 double-free는 없습니다. 소켓을 닫아 두 번의 free를 얻기를 기대했다면 — sctp_association_free()는 transport 목록만 순회하고, rm_peer()는 이미 ghost를 unlink했습니다. 그 문제로 저녁을 낭비하지 마세요.sctp_outq_select_transport()는 chunk의 +0xe8에 있는 chunk->transport에서 +0x15c의 state를 읽습니다. state=1(ACTIVE)로 스프레이된 재할당된 객체는 free 이후 커널 동작을 바꿉니다 — 그 객체가 참조되고 있는 것입니다.sctp_outq_select_transport()가 주입 시점에 stale transport 포인터에서 state=0xFFFF(slab 쓰레기)를 읽는 것을 포착했습니다. 결과: 스프레이는 트리거 이전에 캐시를 미리 groom해야 하며, 이후가 아닙니다 — 이 경로에서는 grace period가 만료되기 전에 객체가 읽힙니다.*(p+0x288)을 p+0x288과 비교합니다. 이는 self-pointer입니다. slab 주소를 모르면 스프레이로 통과할 수 없습니다. 즉 이 버그는 af_specific 제어가 instruction-pointer 제어로 바뀌기 전에 info leak이 필요합니다. af_specific 자체가 간접 호출 지점(*(af_specific+0x18), rdi = transport)이므로, leak이 존재하면 그것이 상입니다.setsockopt(SCTP_AUTH_KEY)가 동일한 kmalloc-1k 캐시에 들어갑니다. 헤더는 struct sctp_authkey { assoc_id; keynumber; keylength; key[] }이며, AUTH는 먼저 sctp_assoc_value와 함께 SCTP_AUTH_SUPPORTED로 소켓별로 활성화해야 합니다 — 평범한 int는 EINVAL을 받습니다.이것은 트리거/크래시 PoC입니다. 권한 상승이 아니며 이 저장소는 그렇게 주장하지 않습니다. VM에서 자신의 커널에 대해 실행하세요.
테스트 환경: 7.1.5+kali-amd64 (Kali rolling) 및 7.0.12+kali-amd64.
로직 중 커널에서 파생된 부분은 GPL-2.0; 나머지는 원하는 대로 하세요. 승인된 보안 테스트 및 연구 전용입니다.
acklock.c — ASCONF-ACK 소프트 락업 (모든 7.1.x에서 미수정)동일한 코드 경로의 두 번째 미패치 커널 버그로, 7.1.5에서
시연됨: sctp_verify_asconf()는 최소 길이가 없는 ERR_CAUSE TLV를
수용하고 PAD4 정렬된 매개변수를 순회하는 반면,
sctp_get_asconf_response()(ASCONF-ACK 소비자)는 RAW 길이만큼
전진합니다. ERR_CAUSE TLV의 길이가 5인 조작된 ASCONF-ACK는
소비자를 3바이트만큼 비동기화합니다; 그 다음 4바이트는 length = 0인
매개변수 헤더로 읽히고, asconf_ack_len -= 0이 영원히
회전합니다 — softirq에서의 무한 루프(ksoftirqd가 100%로 고정, CPU
기아, 7.1.5에서의 3/3 실험실 실행에서 clocksource-watchdog 타임아웃으로
확인됨).
메인라인에서 2cb0b0b1ed69("sctp: fix soft lockup from unpadded
ASCONF-ACK parameter iteration", 2026-08-30)로 수정됨 — linux-7.1.y의
HEAD(7.1.13)에는 없음, 따라서 Kali의 최신을 포함한 모든 릴리스된
7.1.x 커널이 영향을 받습니다. 원격이며, 확립된 association과
net.sctp.addip_noauth_enable=1(또는 같은 창에서의 AUTH 우회)이
필요합니다. poc.c처럼 빌드하고 실행하세요:
gcc -O2 -Wall -o acklock acklock.c && ./acklock
VM을 관찰하십시오: poison ACK 후 몇 초 안에 CPU가 멈춥니다
(clocksource watchdog 타임아웃; perf-NMI-watchdog가 활성화된 커널에서는
sctp_get_asconf_response에서 전형적인 BUG: soft lockup 스플랫이 발생합니다).