
CVE-2026-98432에 대한 증명 전용 PoC로, Linux IPv4/TCP ireq_opt use-after-free 취약점을 다룹니다. 코드 실행 없이 UAF를 확인하는 레이스 트리거와 힙 재사용 오라클입니다.
ireq_opt use-after-free PoCCVE-2026-98432(CVSS 9.8)에 대한 증명 전용 PoC: Linux IPv4/TCP 스택에서 원격으로 트리거 가능한 use-after-free.
승인된 보안 테스트 전용입니다. 소유하거나 테스트에 대한 명시적 허가를 받은 시스템에 대해서만 실행하십시오.
이 PoC는 경쟁 조건(race)을 트리거하고, 커널이 해제된 ip_options_rcu 객체의 바이트를 SYN-ACK로 복사해 내보내는 시점을 탐지합니다. 사용자나 파일을 생성하지 않으며, 코드 실행 체인을 구현하지도 않습니다.
적중(hit)은 서버가 SYN-ACK 안에 다른 할당의 마커를 반환했다는 것을 의미합니다. 정상적인 커널은 그렇게 할 수 없습니다. 커널 6.8.0-147-generic(Ubuntu에서 취약으로 표시됨; 수정은 6.8.0-149에 대기 중)을 사용하는 Ubuntu 24.04.5에서의 통제된 테스트에서, 180초 루프백 실행은 320,829회 시도 중 21회 적중을, 300초 실행은 616,911회 시도 중 22회 적중을 산출했습니다. 커널은 깨끗한 상태를 유지했습니다(tainted=0, oops 없음).
해당 호스트의 패킷 캡처나 로그는 여기에 포함되지 않습니다. PoC 자체의 출력이 증거입니다.
IPv4 옵션을 포함한 SYN은 커널이 요청 소켓(request socket)을 위해 ip_options_rcu(72바이트, kmalloc-96)를 생성하게 합니다. 핸드셰이크가 완료되면, tcp_v4_syn_recv_sock()은 해당 객체를 복사하지 않고 자식 소켓에 넘긴 다음, 요청 소켓의 포인터를 지웁니다. 이미 요청 소켓을 조회한 두 번째 CPU는 여전히 tcp_check_req() → tcp_v4_send_synack() → ip_build_and_send_pkt() → ip_options_build() 내부에 있을 수 있으며, 동일한 객체를 읽고 있을 수 있습니다. 자식 소켓은 SOCK_RCU_FREE가 아니며, inet_sock_destruct()는 inet_opt를 일반 kfree()로 해제하므로, 해당 윈도우에서 자식이 정리되면 리더가 해제된 메모리를 역참조하게 됩니다.
요청 소켓 마이그레이션(net.ipv4.tcp_migrate_req=1 또는 BPF reuseport 프로그램)을 사용하는 두 번째 변형도 존재합니다. 수정은 inet_sock_destruct()와 tcp_v4_reqsk_destructor() 모두에서 kfree_rcu()를 사용합니다.
Linux 4.4(079096f103fa)에서 도입되었으며, 메인라인 7.3에서 수정되어 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56 및 7.2.10으로 백포트되었습니다. 영향: Linux 4.4 이상, 미패치 상태.
영향 범위는 원격 크래시(DoS)에서 커널 메모리 노출, 그리고 전체 익스플로잇 체인을 갖춘 경우 코드 실행에 이릅니다. 이 저장소는 use-after-free를 증명하는 데서 멈춥니다.
시도마다:
ireq_opt가 있는 요청 소켓 생성; 대상은 SYN-ACK에서 해당 옵션을 에코합니다.inet_opt를 상속하고, 요청 소켓은 포인터를 버립니다.오라클. 모든 시도는 옵션 오프셋 16..23에 고유한 32비트 매직과 체크 태그를 삽입합니다. 사용자 공간 리더가 옵션을 보기 전에 세 번의 재작성이 발생합니다:
| 단계 | 슬롯 |
|---|---|
SYN에 대한 서버 ip_options_compile() | 4..7 (TS) / 3..6 (RR) |
SYN-ACK에 대한 서버 ip_options_build() | 8..11 (TS) / 7..10 (RR) |
SYN-ACK에 대한 수신 호스트 ip_options_compile() | 12..15 (TS) / 11..14 (RR) |
마커는 이 세 단계 모두를 지나서 위치합니다. 다른 할당의 매직(tag == magic ^ 0x5a5a5a5a 포함)을 에코하는 SYN-ACK는 리더가 해제 후 재사용된 객체로부터 복사했음을 증명합니다.
CAP_NET_RAW; 자동 RST 필터에는 CAP_NET_ADMIN(root) 필요.accept()하는 IPv4 TCP 리스너가 있는 미패치 Linux.iptables/nftables.--proof는 단일 호스트에서 자체 완결적입니다: 루프백 리스너를 시작하고 경쟁을 일으킵니다.
python3 poc_cve_2026_98432.py --selftest
sudo python3 poc_cve_2026_98432.py --check
실행 중인 커널, 고정 릴리스 테이블 및 관련 sysctl을 출력합니다.
sudo python3 poc_cve_2026_98432.py --proof 9000 \
--duration 180 --workers 6 --spray 2 --spray-rate 1000 \
--pcap /tmp/proof.pcap
127.0.0.1:9000에서 리스너를 시작하고, 레이스를 실행하고, 판정을 출력한 뒤, 리스너를 중지하고 임시 방화벽 규칙을 제거합니다. 호스트에는 아무것도 생성되지 않습니다 — 일시적인 TCP 상태만 존재합니다.
sudo python3 poc_cve_2026_98432.py --probe <TARGET>:<PORT>
하나의 SYN: 대상이 MPTCP 리스너인지, 그리고 IPv4 옵션을 에코하는지(오라클에서 요구됨)를 보고합니다.
sudo python3 poc_cve_2026_98432.py <TARGET>:<PORT> \
--workers 16 --spray 4 --duration 600 --pcap hits.pcap
공용 주소는 --allow-public이 주어지지 않는 한 거부됩니다. bare-RST 필터는 자동으로 설치되며 종료 시 제거됩니다.
python3 poc_cve_2026_98432.py --listen 9000 --mptcp
127.0.0.1에 바인딩되며, 원격 공격자 호스트에 대해서만 --listen-any를 추가합니다. 리스너는 각 연결을 수락한 뒤 즉시 SO_LINGER(0)으로 닫아 자식 프로세스를 빠르게 해제합니다.
| 필드 | 의미 |
|---|---|
attempts | 시작된 핸드셰이크 수 |
synacks | 관찰된 SYN-ACK 수 (시도당 약 2개) |
hits | SYN-ACK가 다른 할당의 마커를 포함 → UAF 확인됨 |
anomalies | 마커는 파싱되었으나 태그 불일치 (부분 손상) |
leaks | 에코된 바이트에서 커널 포인터로 보이는 값 |
noopt | 에코된 옵션이 없는 SYN-ACK (syncookies/큐 가득 참/제거) |
stale | 필터링된 지연 SYN-ACK 재전송 (히트 아님) |
malformed | ihl < 5인 SYN-ACK (stale-optlen 언더플로 시그니처) |
timeouts | --timeout 내에 SYN-ACK 없음 |
spray | 전송된 힙 스프레이 SYN 수 |
히트 샘플:
[!] UAF ORACLE HIT #1 at attempt sport=63042 (expected magic f815ca68 tag a24f9032)
tcp <TARGET>:9000 -> <CLIENT>:63042 seq=c20ed8ef ack=d212b2b1 flags=0x12
SYN-ACK echoed magic d4bcdcc7 tag 8ee6869d, belongs to another allocation
ip options:
44 28 11 00 03 71 b0 b5 03 71 cc 73 03 71 cc 73
d4 bc dc c7 8e e6 86 9d 00 00 00 00 00 00 00 00
0x8ee6869d == 0xd4bcdcc7 ^ 0x5a5a5a5a: 태그 관계는 에코된 바이트가 다른 할당에서 온 유효한 마커임을 증명합니다.
종료 코드: 0 = 증거 관찰됨, 3 = 없음, 2 = 설정 오류,
1 = 사용법/런타임 오류.
이 레이스는 확률적입니다. 테스트 호스트에서 --workers 6 --spray 2 --spray-rate 1000을 최소 3분간 실행하면 재현되었습니다. 같은 호스트에서
--workers 4 --spray 1 --spray-rate 500으로 60초간 실행했을 때는 적중이
전혀 없었습니다. 짧은 실행만으로 "취약하지 않음"이라고 결론 내리지 마십시오.
--duration — 더 많은 시도; 공개된 연구에서는 수십 분이 걸렸습니다.--workers — 더 많은 동시 시도 (LAN에서 8–16).--spray / --spray-rate — kmalloc-96을 계속 회전시켜 해제된 옵션
객체가 재사용되도록 합니다.--lead / --rst-delta / --jitter — 마이크로초 타이밍 스윕; 20/40/10에서
시작하여 lead 0–40, delta 0–80을 스윕하십시오.개발에는 일회용 VM이 더 안전합니다. 두 개의 수정 라인을 되돌려 취약한 6.12.112 커널을 빌드하십시오:
curl -LO https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.112.tar.xz
tar xf linux-6.12.112.tar.xz && cd linux-6.12.112
sed -i 's/kfree_rcu(rcu_dereference_protected(inet->inet_opt, 1), rcu);/kfree(rcu_dereference_protected(inet->inet_opt, 1));/' net/ipv4/af_inet.c
sed -i 's/kfree_rcu(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1), rcu);/kfree(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1));/' net/ipv4/tcp_ipv4.c
make defconfig
make -j"$(nproc)"
QEMU에서 부팅한 뒤 게스트 내부에서 --proof를 실행하거나, 호스트에서 포워딩된 포트를 대상으로 --race를 실행합니다.
트리거에는 옵션이 포함된 IPv4 헤더(ihl > 5)가 필요합니다. 엣지에서 이러한 패킷을 드롭하면 커널이 패치될 때까지 버그에 도달할 수 없습니다. nftables 사용 시:
sudo nft add table ip cve98432
sudo nft add chain ip cve98432 input '{ type filter hook input priority -10; policy accept; }'
sudo nft add rule ip cve98432 input ip hdrlength != 5 drop
패치 후 제거: sudo nft delete table ip cve98432.
패치가 제공되면 적용:
sudo apt update
apt-cache policy linux-image-generic # 6.8.0-149 or newer
sudo apt full-upgrade
sudo reboot
uname -r
sysctl -w net.mptcp.enabled=0는 하나의 악용 경로만 차단할 뿐이며, 완전한 수정은 아닙니다.
CVE-2026-98432는 원래 연구자들에 의해 발견되고 보고되었습니다. CVE 레코드 https://www.cve.org/CVERecord?id=CVE-2026-98432 및 Linux 커널 수정 커밋을 참조하십시오. 이 저장소는 트리거와 오라클에 대한 독립적인 증명 전용 재현입니다. 취약점의 발견에 대해 어떠한 주장도 하지 않습니다.
코드는 MIT 라이선스로 배포됩니다 — LICENSE를 참조하십시오.