CVE-2026-98432 읽기 — SYN 하나에 실린 IP 옵션이 커널 메모리를 두 번 쓰게 되는 경위
우분투 보안 공지 CVE-2026-98432는 2026-10-15 에 공개됐고 10-28 에 마지막으로 갱신됐다. 우분투 우선순위는 High, CVSS 3.1 점수는 9.8 (Critical) 이다. 공격 벡터가 네트워크이고 인증이 필요 없다고 매겨져 있어서 숫자만 보면 무섭다.
이 글은 패치 원문을 기준으로 다음 네 가지를 정리한다.
- 무엇이 고장 났나
- 누가 이 경로를 밟을 수 있나
- 점수가 왜 두 개인가
- 지금 무엇을 할 수 있나
1. 무엇이 고장 났나 — 소유권은 넘겼는데 복사는 안 했다
수정 커밋은 Eric Dumazet 의 e3f33b0a1d89 “ipv4: free inet_opt and ireq_opt after an RCU grace period”다. 날짜는 2026-10-01, 보고자는 Xinyang Ge, 리뷰어는 Kuniyuki Iwashima 다. 메인라인에는 7.3-rc7 에서 들어갔다.
TCP 서버가 SYN 을 받으면 커널은 아직 연결이 아닌 “요청 소켓”(request socket)을 만든다. 이때 SYN 의 IPv4 헤더에 IP 옵션이 붙어 있으면, 그 옵션을 복사해 요청 소켓의 ireq_opt 에 저장한다. SYN-ACK 를 보낼 때 같은 옵션을 되돌려 써야 하기 때문이다. 저장하는 함수 tcp_v4_save_options() 를 보면, 옵션 길이가 0 이면 아무것도 할당하지 않고 NULL 을 돌려준다(include/net/tcp.h).
if (opt->optlen) {
dopt = kmalloc(opt_size, GFP_ATOMIC);
...
}
return dopt;
3-way handshake 가 끝나면 tcp_v4_syn_recv_sock() 이 자식 소켓을 만든다. 이때 ireq_opt 를 복사하지 않고 포인터만 넘긴다. 그 뒤로는 자식 소켓의 inet_opt 가 그 메모리의 주인이다.
문제는 그 사이에 다른 CPU 가 같은 요청 소켓을 아직 쓰고 있을 수 있다는 점이다. 패치 설명에 나온 순서는 이렇다.
- CPU A: 클라이언트의 ACK 를 받아 자식 소켓을 만들고, 옵션 포인터를 자식에게 넘긴다.
- CPU B: 같은 요청에 대해 재전송된 SYN 을 받아
tcp_check_req()에서 SYN-ACK 를 다시 만든다. 옵션은rcu_read_lock()아래에서 읽는다. - 자식 소켓이 곧바로 닫혀 해제된다.
inet_sock_destruct()는inet_opt를 그냥kfree()한다. - CPU B 의
ip_options_build()가 이미 해제된 메모리에서opt->optlen을 (두 번) 읽는다. use-after-free 다.
읽는 쪽은 RCU 규칙을 지키는데 해제하는 쪽이 RCU 유예 기간(grace period)을 기다리지 않은 것이 원인이다. RCU 는 “읽는 쪽은 잠금 없이 읽고, 해제하는 쪽은 그 시점에 읽고 있던 쪽이 다 빠져나갈 때까지 기다린다”는 약속이다(Linux kernel — What is RCU?). 그래서 수정은 두 줄이다. inet_sock_destruct() 와 tcp_v4_reqsk_destructor() 의 kfree() 를 kfree_rcu() 로 바꿨다. 같은 포인터를 바꾸는 다른 경로(do_ip_setsockopt() 등)는 원래부터 kfree_rcu() 를 쓰고 있었다.
왜 10년 동안 숨어 있었나
패치에는 Fixes: 태그가 두 개 붙어 있다.
079096f103fa“tcp/dccp: install syn_recv requests into ehash table” — 2015-10-02, 같은 Eric Dumazet 의 “lockless listener” 연작 17개 중 하나다(LWN 소개). 요청 소켓을 리스너 전용 해시 대신 일반 ehash 에 넣어서, 리스너 잠금 없이 처리하게 했다. 연작 소개글에서는 리스너 하나로 초당 350만 SYN 을 처리했다고 적고 있다(저자의 측정치다). 잠금이 사라지면서 “자식이 해제되는 동안 다른 CPU 가 SYN-ACK 를 만들고 있는” 순간이 가능해졌다.c905dee62232“tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.” — 요청 소켓을 다른 리스너로 옮기는 기능이다. 두 번째 경로가 여기서 생겼다(아래 참고).
우분투 표도 이 연대와 맞는다. 3.13 커널을 쓰는 14.04 는 “Not affected”이고, 4.4 커널의 16.04 부터는 “Vulnerable”이다.
두 번째 경로 — 요청 소켓 이주
net.ipv4.tcp_migrate_req=1 이거나, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE eBPF 프로그램을 쓰면, 리스너가 닫힐 때 대기 중인 요청을 같은 포트의 다른 리스너로 옮긴다. 이때 옛 요청 소켓과 복제본이 같은 ireq_opt 를 공유하므로 같은 종류의 경쟁이 생긴다. tcp_migrate_req 의 기본값은 0 (꺼짐) 이다(kernel docs — ip-sysctl).
IPv6 는 첫 번째 경로의 영향이 없다. tcp_v6_syn_recv_sock() 이 옵션을 복사하기 때문이다. 이주 경로에는 같은 문제가 있지만 CALIPSO 를 쓸 때만 해당되며, 패치에 따르면 별도 패치로 고칠 예정이다.
2. 누가 이 경로를 밟을 수 있나
위 코드에서 바로 나오는 조건은 다음과 같다(코드를 읽고 내린 해석이다. 공지에 공격 시나리오가 따로 적혀 있지는 않다).
- IPv4 TCP 로 리스닝 중인 포트가 있어야 한다.
- 그 포트에 IP 옵션이 붙은 SYN 이 도착해야 한다. 옵션이 없으면
ireq_opt가NULL이라 해제할 것 자체가 없다. - 같은 요청의 SYN 재전송과 자식 소켓 해제가 다른 CPU 에서 겹치는 타이밍이 맞아야 한다.
IP 옵션이 붙은 패킷은 인터넷 경로에서 잘 살아남지 못한다. 많은 라우터·방화벽이 이런 패킷을 버리거나 느린 경로로 보낸다. IETF 는 옵션 종류별로 필터링 권고를 따로 정리해 두었다(RFC 7126). 다만 “대부분의 경로가 버린다”는 것은 확률일 뿐 보장이 아니다. 같은 LAN 안이나 직접 연결된 구간에서는 옵션이 그대로 도착한다.
3. 점수가 왜 두 개인가 — 9.8 과 8.3
같은 우분투 페이지에 점수가 두 개 있다.
| 기준 | 점수 | 벡터 |
|---|---|---|
| CVSS 3.1 | 9.8 Critical | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CVSS 4.0 | 8.3 High | AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:H/... |
차이는 4.0 에 새로 생긴 AT(Attack Requirements) = P(Present) 와 영향 평가에서 온다. CVSS 4.0 명세는 AT 를 “공격자가 통제할 수 없는, 공격 성공에 필요한 배포·실행 조건”으로 정의하고, 경쟁 조건(race condition)을 이겨야 하는 경우를 대표 예로 든다(FIRST — CVSS v4.0 Specification). 1번에서 본 “다른 CPU 에서 타이밍이 겹쳐야 한다”가 바로 그 조건이다. 기밀성·무결성 영향도 4.0 에서는 Low 로 내려가 있고, 가용성(커널 크래시)만 High 다.
정리하면 3.1 점수는 “최악의 경우 원격 코드 실행”을 기준으로 매긴 값이고, 4.0 점수는 “경쟁을 이겨야 하고, 현실적인 피해는 주로 서비스 중단”이라는 판단을 반영한 값이다. 어느 쪽이 맞는지 판정할 공개 익스플로잇이나 중립적인 재현 보고는 이 글을 쓰는 시점에 찾지 못했다.
4. 지금 무엇을 할 수 있나
수정 커널의 현황 (2026-10-29 기준)
kernel.org 안정판에는 이미 들어갔다. 각 버전의 ChangeLog 에 commit e3f33b0a… upstream 으로 백포트가 기록돼 있다.
우분투 패키지는 아직이다. 공지의 상태표는 이렇다.
| 릴리스 | 패키지 | 상태 |
|---|---|---|
| 26.04 LTS | linux | Vulnerable — 7.0.0-43.43 에서 작업 중 |
| 24.04 LTS | linux | Vulnerable — 6.8.0-149.149 에서 작업 중 |
| 24.04 LTS | linux-hwe-6.17, linux-hwe-7.0 | Vulnerable |
| 22.04 LTS | linux | Vulnerable — 5.15.0-203.213 에서 작업 중 |
“work in progress” 는 수정 패키지가 아직 배포되지 않았다는 뜻이다. 지금 apt upgrade 를 해도 이 CVE 는 닫히지 않는다. 패키지가 나오면 설치하고 재부팅해야 실제로 반영된다. 커널은 재부팅 전까지 옛 이미지로 돈다.
우리 클러스터에 대입하면
2026-10-29 22:13 KST 에 각 노드에서 실측한 값이다.
| 노드 | OS | 커널 |
|---|---|---|
| ilwon | Ubuntu 26.04.1 LTS | 7.0.0-38-generic |
| lemuel | Ubuntu 24.04.5 LTS | 6.8.0-147-generic |
| louise | Ubuntu 24.04.5 LTS | 6.8.0-139-generic |
| solomon | Ubuntu 26.04 LTS | 7.0.0-31-generic (kubectl 기준) |
네 대 모두 위 표의 “Vulnerable” 행에 해당한다. 우분투가 작업 중이라고 밝힌 버전(7.0.0-43, 6.8.0-149)보다 모두 낮다.
이 공지를 보고 22:13~22:15 에 ilwon·lemuel·louise 세 대를 systemctl poweroff 로 정상 종료했다. solomon 은 10/27 부터 이미 꺼져 있었다. 꺼진 노드는 SYN 을 받지 않으니 그동안 이 경로는 닫혀 있다. 하지만 다시 켜면 같은 커널로 부팅한다. 전원을 끄는 것은 패치를 기다리는 동안의 조치일 뿐, 수정은 아니다. tcp_migrate_req 값은 끄기 전에 확인하지 못했다.
노출 면에서 보면, 이 클러스터의 공개 서비스는 Cloudflare Tunnel 로 들어온다. 노드의 cloudflared 가 밖으로 연결을 맺는 구조라, 인터넷의 SYN 이 노드의 리스닝 포트에 직접 도착하는 경로는 아니다. 남는 면은 같은 LAN 과, 이 글에서 확인하지 않은 공유기 포트포워딩이다.
할 일
- 우분투 패키지 배포를 지켜본다. 공지 페이지의 상태가 “Released (버전)”으로 바뀌면 그 버전 이상을 설치하고 노드를 하나씩 재부팅한다. 재부팅 후
uname -r로 확인한다. tcp_migrate_req를 켜지 않는다. 기본값이 0 이니, 켠 적이 없다면 두 번째 경로는 닫혀 있다.sysctl net.ipv4.tcp_migrate_req로 확인할 수 있다.- IP 옵션 패킷 필터링은 보조 수단이다. 경계에서 IP 옵션이 붙은 패킷을 버리면 첫 번째 경로의 입구가 좁아진다. 다만 공지가 권고한 회피책은 아니고, 정상 트래픽에 미치는 영향은 환경마다 다르다. 이 글은 이것을 해결책으로 권하지 않는다.
정리
- 원인: 2015년에 리스너 잠금을 없앤 성능 개선이 “읽는 쪽은 RCU, 해제는 즉시
kfree()“라는 불일치를 남겼다. 그 불일치가 10년 뒤에 발견됐다. - 수정:
kfree→kfree_rcu두 줄. - 조건: IPv4, IP 옵션이 붙은 SYN, 그리고 경쟁 조건. 점수가 9.8 과 8.3 으로 갈리는 것도 이 조건을 얼마나 무겁게 보느냐의 차이다.
- 우분투: 2026-10-29 현재 수정 패키지는 아직 없다. 나오면 설치하고 재부팅해야 닫힌다.
References
1차 출처
- Ubuntu Security — CVE-2026-98432 (우선순위·CVSS 3.1/4.0·릴리스별 상태, 2026-10-28 갱신본)
- CVE.org — CVE-2026-98432 record
- Linux kernel — commit e3f33b0a1d89 “ipv4: free inet_opt and ireq_opt after an RCU grace period” (Eric Dumazet, 2026-10-01, 7.3-rc7)
- kernel.org 안정판 ChangeLog — 7.2.10 · 6.18.56 · 6.6.159 · 5.15.223
- Eric Dumazet, netdev — [PATCH net-next 10/17] tcp/dccp: install syn_recv requests into ehash table (2015-10-02, 원인 커밋)
- LWN — tcp/dccp: lockless listener (연작 소개)
- Linux kernel source —
tcp_v4_save_options()in include/net/tcp.h
명세·문서
- Linux kernel documentation — IP Sysctl: tcp_migrate_req (기본값 0)
- Linux kernel documentation — What is RCU?
- FIRST — CVSS v4.0 Specification (Attack Requirements)
- IETF — RFC 7126: Recommendations on Filtering of IPv4 Packets Containing IPv4 Options
익스플로잇 공개 여부와 실제 악용 사례는 작성 시점에 1차 출처에서 확인하지 못했다. 이 글은 공격 방법을 다루지 않는다.