Huny's Dev Blog
huny.dev

Linux 커널 UDP 소켓 UAF 취약점(CVE-2026-98276) 해부: sock_gettstamp()와 bind() 경합이 SOCK_RCU_FREE를 지우는 원리와 패치 점검하기

실제 운영 서버 공격에 악용된 Linux 커널 UDP 소켓 Use-After-Free 취약점(CVE-2026-98276)의 비원자적 sk_flags 경합 원리와 SOCK_RCU_FREE 증발 메커니즘을 정리한다. 배포판별 커널·라이브패치 현황과 크래시 로그 점검 절차를 살펴본다.

Hun Jang
Hun Jang Nov 9, 2026

bind()의 SOCK_RCU_FREE 설정과 sock_gettstamp()의 SOCK_TIMESTAMP 설정이 단일 메모리 워드(sk_flags)에서 충돌하지 않도록 첫 호출 소켓 잠금으로 직렬화해 RCU 유예 기간을 보존하는 개념 일러스트다. 실제 커널 디버거 화면이 아니다.

Linux 커널의 UDP 소켓 코드에서 비권한 로컬 사용자가 두 개의 시스템 콜(bind()와 SIOCGSTAMPNS_NEW 등 타임스탬프 ioctl)을 경합시켜 커널 패닉을 유발하거나 루트(root) 권한 상승으로 이어질 수 있는 Use-After-Free 취약점인 CVE-2026-98276(커널 CNA 기준 CVSS 3.1 7.8 High, Red Hat 기준 7.0)이 실제 운영 서버 공격에 악용된 사실이 2026년 11월 7일 공개되었다. 업스트림 커널에서는 커밋 9ed55f3dbef4("net: lock the socket in sock_gettstamp()")로 먼저 수정되었으나, 엔터프라이즈 배포판(RHEL 7~10 등)에 패치가 도달하기 전인 11월 4일 실제 상용 CloudLinux 8 서버가 이 공격으로 12회 연속 재부팅되는 사고가 발생하면서 긴급 커널 업데이트와 라이브패치가 배포되고 있다. CVE-2026-98276 공식 레코드, CloudLinux 기술 권고문, TuxCare 사건 분석, AlmaLinux 테스팅 커널 공지

결론부터 말하면 이 취약점은 방화벽, sysctl, 부트 파라미터, 혹은 파일시스템 격리(CageFS 등)로 우회 완화(Mitigation)할 수 있는 방법이 전혀 없다. 네트워크 외부 패킷이 아니라 로컬 프로세스가 자신이 연 UDP 소켓에 대해 호출하는 커널 내부 경로에서 발생하므로, 커널 패키지 업데이트나 무재부팅 라이브패치(KernelCare 등)를 즉시 적용해야 한다. 특히 라이브패치 적용 여부를 확인할 때 kcarectl --info만 실행하면 CVE 목록이 출력되지 않아 패치 여부를 오판할 수 있으므로 반드시 kcarectl --patch-info | grep CVE-2026-98276으로 검증해야 한다.

이 글은 NVD CVE-2026-98276 상세 기록과 SentinelOne 취약점 분석, CloudLinux·TuxCare의 공지를 바탕으로 비원자적 비트 연산 경합 원리와 배포판별 패치 확인 방법을 정리한 가이드다. 실제 커널 크래시 익스플로잇 바이너리는 실행하지 않았으며, 로컬 Python 검증 코드는 단일 메모리 워드(sk->sk_flags)의 Read-Modify-Write 손실과 락 직렬화 동작만 시뮬레이션한다.

1. 왜 소켓 잠금 없이 sk_flags 비트를 켜면 SOCK_RCU_FREE가 사라지는가

Linux 커널의 struct sock 구조체는 소켓의 상태 플래그들을 단일 메모리 워드인 sk->sk_flags에 모아 두고, __set_bit()·__clear_bit() 기반의 비원자적(Non-atomic) 비트 연산인 sock_set_flag()로 수정한다. 커널 설계 규칙상 sk->sk_flags를 수정하는 모든 코드는 소켓 잠금(lock_sock(sk))을 먼저 획득해야 한다.

문제는 패킷 도착 시각을 조회하는 SIOCGSTAMP / SIOCGSTAMPNS_NEW ioctl의 커널 핸들러인 sock_gettstamp()가 소켓 잠금을 잡지 않은 채 SOCK_TIMESTAMP 비트를 켰다는 점이다.

실행 순서 (시간축)
CPU 0: bind() → udp_lib_get_port()
CPU 1: ioctl(SIOCGSTAMPNS_NEW) → sock_gettstamp()
메모리 상태 및 결과 (Use-After-Free 발생 과정)
1단계: 초기 플래그 읽기
sk_flags 워드 값 F를 레지스터로 읽음
소켓 잠금 없이 동일한 초기 sk_flags 워드 값 F를 레지스터로 읽음
두 CPU 모두 SOCK_RCU_FREE와 SOCK_TIMESTAMP가 꺼진 상태(F)를 기준으로 계산 시작
2단계: 독립적인 비트 OR 계산
F | BIT(SOCK_RCU_FREE) 계산 후 sk_flags에 기록하고, sk_add_node_rcu()로 UDP 해시 테이블에 소켓 공개
sock_enable_timestamp() 안에서 F | BIT(SOCK_TIMESTAMP) 계산
이 시점에 UDP 수신 경로(Lockless RCU 리더)가 소켓을 조회할 수 있게 됨
3단계: 덮어쓰기(Lost Update)
bind() 완료
잠금이 없으므로 자신이 읽었던 옛날 값 기준의 F | BIT(SOCK_TIMESTAMP)를 sk_flags에 덮어씀
SOCK_RCU_FREE 비트가 증발함! 소켓은 이미 RCU 해시 테이블에 공개되어 있는데 RCU 보호 플래그만 지워짐
4단계: 소켓 종료 및 UAF
소켓을 close()하면 sk_destruct()가 SOCK_RCU_FREE 비트를 확인함
비트가 없으므로 RCU 유예 기간(Grace period)을 기다리지 않고 소켓 메모리를 즉시 해제(Free)함
동시에 도착한 UDP 패킷을 처리하던 수신 경로(udp_queue_rcv_one_skb → ipv4_pktinfo_prepare)가 이미 해제된 소켓 메모리를 읽어 커널 패닉 및 UAF 발생

Eric Dumazet이 작성한 업스트림 패치(9ed55f3dbef4)는 매번 잠금을 잡는 성능 저하를 피하기 위해, SOCK_TIMESTAMP 플래그가 아직 켜지지 않은 첫 호출 시점에만 sock_gettstamp() 안에서 소켓 잠금을 획득하도록 수정해 bind()와의 경합을 제거했다.

2. 왜 sysctl·방화벽·컨테이너 격리로 막을 수 없으며 실제 영향은 무엇인가

이 취약점이 클라우드 호스팅과 멀티테넌트 컨테이너 인프라에서 특히 위험한 이유는 두 가지 경계 때문이다.

  1. 네트워크 공격이 아니라 로컬 시스템 콜 경합이다: 공격자는 외부에서 특수한 패킷을 보낼 필요가 없다. 서버 내부의 일반 계정(또는 취약한 웹 애플리케이션·컨테이너 하나를 통해 명령 실행 권한을 얻은 공격자)이 자기 자신에게 UDP 소켓을 열고 두 스레드에서 bind()와 ioctl()을 반복 호출하기만 하면 된다. 따라서 외부 방화벽, WAF, CageFS 같은 파일시스템 격리는 이 호출을 전혀 가로채지 못하며, 이를 비활성화하는 sysctl이나 커널 부트 옵션도 존재하지 않는다.
  1. 검증된 서버 전체 마비(DoS)와 권한 상승(LPE) 가능성: TuxCare와 CloudLinux의 보고에 따르면 11월 4일 공격받은 상용 서버는 kernel.panic_on_oops = 1 설정에 의해 12번 연속 전체 재부팅을 겪었다. 또한 CloudLinux 엔지니어들은 연구 환경에서 단순 커널 크래시뿐 아니라 로컬 루트(root) 권한 상승까지 재현했음을 확인했다.

3. 배포판 및 커널 스트림별 패치 현황과 적용·검증 명령어

2026년 11월 8일 기준 주요 Linux 배포판 및 엔터프라이즈 커널 스트림별 수정 현황은 다음과 같다.

배포판 / 커널 스트림
영향 여부
수정 커널 버전 및 라이브패치 제공 상태 (2026-11-08 기준)
즉시 적용 방법 요약
Debian 12 (bookworm) / 13 (trixie)
영향 받음
Debian 13: DSA-6568-1 (10월 16일 배포 완료)
Debian 12: DLA-4841-1 (10월 17일 배포 완료) + KernelCare 라이브패치 제공
apt update && apt upgrade 후 재부팅 또는 KernelCare 업데이트
AlmaLinux 8 / 9 / 10 및 CloudLinux 9 / 10
영향 받음
AlmaLinux 테스팅 저장소에 수정 커널 선배포:
EL9: kernel-5.14.0-687.66.2.el9_8
EL10: kernel-6.12.0-211.74.2.el10_2
almalinux-release-testing 활성화 후 dnf update 'kernel*' 및 재부팅 (정식 저장소 승격 전 긴급 적용 시)
CloudLinux 7h / 8 (Kernel 4.18)
영향 받음
수정 커널 4.18.0-553.184.1.lve.1 단계적 배포 중
CloudLinux 8용 KernelCare 무재부팅 라이브패치 배포 완료 (11월 6일~)
재부팅 없이 즉시 적용 시 kcarectl --update, 커널 교체 시 --enablerepo=cloudlinux-rollout-10-bypass로 업데이트 후 재부팅
CloudLinux 8 LTS / 9 LTS (TuxCare ELS)
영향 받음
안정 채널에 수정 커널 배포 완료:
EL8 LTS: kernel-lts-5.14.0-284.1101.el8.tuxcare.11.els17 이상
EL9 LTS: kernel-lts-5.14.0-284.1101.el9.tuxcare.11.els17 이상
yum update 'kernel*' 후 재부팅 (uname -r에서 els17 이상 확인)
Red Hat Enterprise Linux (RHEL 7~10)
영향 받음
11월 7일 기준 Red Hat 공식 수정 커널 미출시 (Moderate 7.0 분류)
신뢰할 수 없는 로컬 계정·쉘·크론 실행을 제한하고 업스트림/라이브패치 피드 추적

서버에서 라이브패치 적용 여부를 확인하고, 과거 비정상 재부팅이 이 취약점 공격 때문이었는지 커널 로그를 점검하는 명령어는 다음과 같다.

4. 로컬 Python으로 검증하는 비원자적 플래그 손실(Lost Update)과 락 직렬화

왜 단일 메모리 워드(sk_flags)에 대한 비원자적 비트 설정이 이미 켜진 SOCK_RCU_FREE를 지워버리는지, 그리고 첫 호출 시 소켓 잠금을 잡는 수정이 어떻게 이를 막는지 로컬 Python 코드로 시뮬레이션했다.

직접 실행해 확인한 범위

로컬 환경에서 세 가지 검증 케이스를 실행해 결과를 확인했다.

  1. 잠금 없는 경합(locked_first_call=False): tstamp_set은 True가 되지만 rcu_free_preserved가 False로 덮어써지면서 소켓 종료 시 uaf_on_close_during_udp_rx: True가 발생한다.
  1. 첫 호출 소켓 잠금 직렬화(locked_first_call=True): 두 비트(SOCK_RCU_FREE, SOCK_TIMESTAMP)가 모두 보존되어 UAF 조건이 False로 차단된다.
  1. 라이브패치 검증 명령 구분: kcarectl --info 호출은 CVE 리스트 누락(kcarectl_info_omits_cve_list)으로 거부하고 kcarectl --patch-info 출력에서만 CVE-2026-98276 존재 여부를 확인한다.

5. 인프라 운영자를 위한 실무 점검 체크리스트

  • 공유 호스팅·멀티테넌트 컨테이너 노드 최우선 패치: 단일 컨테이너나 저권한 웹 계정 하나가 침해되면 호스트 커널 전체를 패닉시키거나 권한 상승을 시도할 수 있으므로, 외부 사용자 코드나 워크로드를 실행하는 노드의 커널부터 업데이트한다.
  • 연관된 비잠금 sk_flags 변경 경로 후속 패치 주시: 업스트림 커밋(9ed55f3dbef4) 메시지는 sock_gettstamp() 외에도 sk_set_memalloc()과 sk_clear_memalloc() 역시 소켓 잠금 없이 sk_flags를 수정하고 있어 별도 감사 및 후속 패치가 예정되어 있음을 밝히고 있다(호출자: nbd, iSCSI, NVMe over TCP, sunrpc, WireGuard). 커널 보안 공지를 지속적으로 추적해야 한다.
  • 영속적 저널(Storage=persistent) 활성화: kernel.panic_on_oops = 1로 설정된 서버가 공격으로 재부팅될 때 직전 부팅의 크래시 콜스택(journalctl -k -b -1)을 남기려면 /var/log/journal 영속 저장이 활성화되어 있어야 한다.

참고 자료

확인 기준일: November 9, 2026

English version: Linux Kernel UDP Socket UAF (CVE-2026-98276): How sock_gettstamp() and bind() Race to Drop SOCK_RCU_FREE and How to PatchLinux Kernel UDP Socket UAF (CVE-2026-98276): How sock_gettstamp() and bind() Race to Drop SOCK_RCU_FREE and How to Patch

추천 글