
비특권 환경에서 실행되는 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)로 평가하며, 서드파티 트래커는 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()가 반환되면 는 오래된 포인터에 대해 동작합니다. 기본 커널에서 가시적인 효과는 방금 해제된 transport의 주소로 HEARTBEAT가 전송되는 것이며, KASAN에서는 slab-use-after-free 보고서가 나타납니다.
sctp_sf_do_asconf()ASCONF 청크 하나면 충분합니다:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
루트 권한 불필요. PoC는 사용자 + 네트워크 네임스페이스를 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가 흥미로운 부분입니다. 해당 패킷은 커널이 댕글링 new_transport를 탐색했기 때문에만 존재합니다. HEARTBEAT가 out-of-the-blue로 돌아왔기 때문에 클라이언트는 바로 직후 ABORT합니다.
이 중 어느 것도 새로운 연구는 아니지만, 문서화가 부족해서 하루 오후를 소모하게 만들 뿐이므로 다음 사람을 위해 여기에 기록합니다:
sctp_rcv()는 체크섬을 검증하고 불일치 시 패킷을 버립니다. 체크섬 필드를 0으로 설정한 상태에서 전체 SCTP 패킷에 대해 CRC32C(poly 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 bytes) | 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 오프셋(flowi를 잘못 세어 0x68)이 있었습니다. BTF가 근거 자료입니다.
sctp_transport_destroy_rcu)이며, 이 경로에는 double-free가 없습니다. 소켓을 닫아 두 번의 해제를 얻으려 했다면 — sctp_association_free()는 transport 목록만 탐색하며, rm_peer()는 이미 고스트를 연결 해제했습니다. 그걸로 밤을 새우지 마세요.sctp_outq_select_transport()는 청크의 +0xe8에 있는 chunk->transport에서 +0x15c 오프셋의 state를 읽습니다. state=1(ACTIVE)로 스프레이된 재확보 객체는 해제 후 커널 동작을 변경합니다 — 객체가 참조되고 있습니다.state=0xFFFF(slab 가비지)를 읽는 sctp_outq_select_transport()를 포착했습니다. 결과: 스프레이는 트리거 이후가 아닌 이전에 캐시를 사전 정리해야 합니다 — 이 경로에서는 유예 기간이 만료되기 전에 객체가 읽히기 때문입니다.*(p+0x288)을 p+0x288과 비교합니다. 이는 자기 참조 포인터입니다. slab 주소를 모르는 한 스프레이로 이를 통과할 수 없습니다. 즉, 이 버그는 af_specific 제어가 명령 포인터 제어로 바뀌기 전에 정보 유출이 필요합니다. af_specific 자체는 간접 호출 지점(*(af_specific+0x18), rdi = transport)이므로, 유출이 존재하면 그것이 목표물입니다.setsockopt(SCTP_AUTH_KEY)는 동일한 kmalloc-1k 캐시에 들어갑니다. 헤더가 struct sctp_authkey { assoc_id; keynumber; keylength; key[] }임을 기억하고, AUTH는 먼저 SCTP_AUTH_SUPPORTED와 sctp_assoc_value로 소켓별로 활성화해야 합니다 — 일반 int는 EINVAL을 반환합니다.이것은 트리거/크래시 PoC입니다. 권한 상승이 아니며 이 저장소는 그런 주장을 하지 않습니다. VM에서 자신의 커널을 대상으로 실행하세요.
테스트 환경: 7.1.5+kali-amd64(Kali rolling) 및 7.0.12+kali-amd64.
로직의 커널 파생 부분은 GPL-2.0입니다. 나머지는 원하는 대로 사용하세요. 승인된 보안 테스트 및 연구 목적으로만 사용하세요.