01개요
넷필터(netfilter)는 리눅스 커널이 패킷을 처리하는 각 지점(훅)마다 등록된 함수들을 호출해 '이 패킷을 어떻게 할지' 판정(verdict)을 받는 프레임워크입니다. nf_tables는 이 넷필터 위에서 동작하는 최신 방화벽/패킷 필터링 서브시스템으로, nft 명령이나 netlink API로 사용자가 직접 규칙을 등록합니다.
CVE-2024-1086은 이 규칙에 포함되는 '즉시 판정(immediate verdict)' 값을 커널이 받아들이는 코드(nft_verdict_init())가, 판정값의 일부(상위 16비트)를 전혀 검증하지 않는 데서 시작합니다. 이 값이 넷필터 훅 처리 루프(nf_hook_slow())까지 흘러가면, '패킷을 버렸다(drop)'는 판정과 '패킷을 받아들였다(accept)'는 판정이 같은 정수로 겹쳐 읽히는 순간이 생기고, 그 결과 이미 해제된 네트워크 버퍼가 한 번 더 해제됩니다(use-after-free/double-free, CWE-416).
이 취약점은 로컬 권한상승(local privilege escalation)에 쓰입니다 — 원격에서 직접 트리거되지는 않지만, 비특권 사용자 네임스페이스를 통해 CAP_NET_ADMIN을 얻을 수 있는 환경(다수의 리눅스 배포판 기본 설정, 컨테이너 런타임 포함)에서는 일반 사용자가 이 경로로 root 권한까지 올라갈 수 있습니다. 2024년 5월 CISA 긴급취약점목록(KEV)에 등재됐고, 2025년 10월에는 실제 랜섬웨어 공격 체인에서 이 취약점이 쓰였다는 사실이 확인됐습니다.
CVSS: 7.8 (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, NVD) · CWE-416(Use After Free)
이 근본원인은 2014년 2월 커밋 e0abdadcc6e1("netfilter: nf_tables: accept QUEUE/DROP verdict parameters")에서 들어갔고, 2024년 1월 20일 커밋 f342de4e2f33e0e39165d8639387aa6c19dff660으로 그 커밋 전체가 되돌려지며 고쳐졌습니다(발견·신고자: Notselwyn, pwning.tech). NVD 발행일은 2024-01-31입니다. 별개 CVE로 쪼개진 변형(variant)은 확인되지 않았습니다 — 이 CVE 하나가 이 근본원인 전체를 대표합니다.
02영향받는 버전
| 대상 | 취약 버전 | 안전 버전 |
|---|---|---|
| Linux 커널(mainline) | v3.15 ~ v6.8-rc1 (커밋 e0abdadcc6e1이 들어간 이후 전체) | 커밋 f342de4e2f33e0e39165d8639387aa6c19dff660 포함 버전 |
| stable/longterm 백포트 | 5.15.148 이하 / 6.1.75 이하 / 6.6.14 이하 / 6.7.2 이하 | 5.15.149 이상 / 6.1.76 이상 / 6.6.15 이상 / 6.7.3 이상 |
| Ubuntu(대표 예시, 배포판마다 백포트 버전 상이) | 22.04 LTS: 5.15.0-101.111 미만 등 | 각 배포판 보안공지의 패치 버전 이상(벤더별 개별 확인 필요) 분석 — 배포판별 백포트 시점은 벤더 공지 기준, 전수 확인은 하지 않음 |
03배경 — 판정값 하나에 두 가지 정보를 욱여넣은 인코딩
넷필터 훅 함수는 패킷마다 unsigned int 하나로 판정을 반환합니다. 이 정수는 include/uapi/linux/netfilter.h에서 두 부분으로 겹쳐 인코딩됩니다 — 하위 1바이트(NF_VERDICT_MASK = 0x000000ff)는 NF_ACCEPT(1)·NF_DROP(0)·NF_QUEUE(3) 같은 '무엇을 할지'를, 상위 16비트(NF_VERDICT_QMASK = 0xffff0000)는 NF_QUEUE일 땐 큐 번호를, NF_DROP일 땐 (선택적으로) 에러코드를 담습니다. 헤더 주석 자체가 이걸 "nice하지 않지만, 추가 함수 인자를 두는 것보다는 낫다"고 표현할 만큼 의도된 절충입니다.
nf_tables에서는 이 판정값이 규칙의 '즉시(immediate)' 문(예: accept, drop, jump target)을 통해 사용자 공간(userspace)에서 netlink로 직접 전달됩니다. nft_immediate_init()이 이 값을 받아 nft_data_init()을 거쳐 nft_verdict_init()으로 넘기고, 이 함수가 그 값을 struct nft_data의 verdict.code 필드에 채워 넣습니다 — 이 필드가 나중에 훅이 실제로 반환하는 값이 됩니다.
여기서 핵심은 이 값이 커널이 계산한 값이 아니라 사용자가 netlink로 직접 써 보낸 32비트 정수라는 점입니다. 이 값을 커널의 다른 계층(nf_hook_slow(), NF_HOOK())이 얼마나 엄격하게 검증하고 받아들이는지가 다음 절의 단서입니다.
04취약점 발생 원인
이 취약점은 서로 다른 두 계층이 같은 32비트 정수를 서로 다른 전제로 읽으면서 성립합니다. 상위 16비트를 검증하지 않고 저장한 것(W1)과, 그 상위 비트를 무조건 유효한 값으로 믿고 계산에 쓴 것(W2)이 만나야 완성됩니다.
1"하위 1바이트만 확인하고, 상위 16비트는 그대로 저장했다"
nft_verdict_init()의 스위치문이 data->verdict.code & NF_VERDICT_MASK(하위 1바이트)만 NF_ACCEPT/NF_DROP/NF_QUEUE인지 확인하고, 원본 32비트 값 전체를 그대로 verdict.code에 저장합니다.
nft_verdict_init()은 netlink 속성에서 data->verdict.code = ntohl(nla_get_be32(...))로 32비트 값을 그대로 읽어들인 뒤, 이 값이 유효한지 스위치문으로 검사합니다. 문제는 검사 대상이 원본 32비트 전체가 아니라 data->verdict.code & NF_VERDICT_MASK — 즉 하위 1바이트만이라는 것입니다.
Linux 커널 오픈소스 실제 소스 — net/netfilter/nf_tables_api.c · nft_verdict_init() (패치 전, 커밋 b462579b2b86a8f5230543cadd3a4836be27baf7 = 패치 커밋 f342de4e2f33e0e39165d8639387aa6c19dff660의 부모). 10996행의 마스킹이 하위 1바이트만 검증하는 지점입니다.
이 마스킹된 값이 NF_ACCEPT·NF_DROP·NF_QUEUE 중 하나이기만 하면, 검증되지 않은 상위 16비트를 포함한 원본 32비트 전체가 그대로 verdict.code에 저장된 채 통과합니다. 즉 하위 1바이트는 화이트리스트로 걸러지지만, 상위 16비트는 사실상 사용자가 원하는 어떤 값이든 넣을 수 있습니다.
왜 이렇게 됐나이 코드는 '판정 코드가 커널이 아는 값인가'라는 질문에만 답하도록 설계됐습니다. 판정값이 상위 비트에 부가 정보(에러코드·큐 번호)를 함께 실어 나른다는 사실, 그리고 그 부가 정보를 사용자가 임의로 채울 수 있다는 사실은 이 함수가 검증할 질문으로 다뤄지지 않았습니다.
2"상위 16비트는, 유효한 에러코드일 거라고 믿었다"
nf_hook_slow()가 NF_DROP 판정을 처리할 때, 이미 버퍼를 해제한 뒤 상위 16비트를 NF_DROP_GETERR()로 부호 있는 정수로 변환해 그대로 반환하는데, 이 반환값을 상위 호출자(NF_HOOK())가 '1이면 훅이 패킷을 통과시켰다'는 별개의 계약으로 재해석합니다.
nf_hook_slow()는 하위 1바이트로 분기한 뒤 NF_DROP이면 kfree_skb_reason()으로 패킷 버퍼(skb)를 즉시 해제합니다. 그다음 NF_DROP_GETERR(verdict)를 호출해 상위 16비트를 -(verdict >> 16)로 계산한 값을 반환값으로 씁니다 — 이 값은 원래 'drop한 이유(에러코드)'를 호출자에게 알려주기 위한 것입니다.
Linux 커널 오픈소스 실제 소스 — net/netfilter/core.c · nf_hook_slow() (커밋 b462579b2b86a8f5230543cadd3a4836be27baf7, 패치 대상이 아니라 이 취약점의 두 번째 축 — 이 함수 자체는 이 CVE로 수정되지 않았습니다). 631~633행이 '해제 후 상위비트를 에러코드로 신뢰'하는 지점입니다.
그런데 W1 때문에 상위 16비트가 검증되지 않은 사용자 값이므로, 이 계산 결과가 우연히 1 — 즉 NF_ACCEPT와 같은 정수 — 이 될 수 있습니다(예: 상위 16비트를 0xffff로 채우면, 부호 있는 정수 연산에서 -(-1) = 1이 됩니다). nf_hook_slow()를 호출하는 NF_HOOK() 매크로는 반환값이 정확히 1일 때 '이 훅이 패킷을 받아들였으니 다음 처리 함수(okfn)를 마저 실행하라'는 완전히 다른 계약으로 이 숫자를 읽습니다. 그 결과 이미 kfree_skb_reason()으로 해제된 skb가 okfn() 경로에서 정상 패킷인 것처럼 다시 처리됩니다 — 사용후해제이자, 그 경로가 다시 skb를 해제하면 더블프리입니다.
왜 이렇게 됐나nf_hook_slow()·NF_DROP_GETERR()·NF_HOOK() 세 조각은 각자 합리적인 계약 위에서 짜였습니다. 문제는 '상위 16비트=에러코드'라는 계약과 '반환값 1=훅이 통과시킴'이라는 계약이 같은 정수 공간을 공유한다는 점입니다. 이 두 계약이 우연히 충돌하는 값(에러코드로 해석하면 1이 되는 상위 비트)이 존재한다는 것 자체가 이 인코딩 방식의 구조적 위험이고, W1이 그 값을 사용자가 고를 수 있게 열어준 것입니다.
무게 중심은 W1(nft_verdict_init()이 상위 16비트를 검증하지 않은 것)입니다.
W2(nf_hook_slow()가 상위 비트를 에러코드로 신뢰하고, NF_HOOK()이 반환값 1을 '통과'로 해석하는 것)는 그 자체로는 결함이 아닙니다 — 판정값 인코딩 규약이 원래 그렇게 설계됐고, 이 함수들은 2024년 패치에서도 바뀌지 않았습니다. W1이 사용자에게 이 상위 비트에 대한 통제권을 열어주지만 않았다면 두 계약은 절대 충돌하지 않았습니다.
한 문장으로 묶으면 이렇습니다. 한 정수 안에 두 가지 다른 의미(무엇을 할지 / 부가 정보)를 겹쳐 담는 인코딩에서, 그중 절반(상위 비트)의 입력 검증을 빠뜨렸습니다. 그 틈에서 '버림'을 뜻하는 값과 '받아들임'을 뜻하는 값이 같은 숫자로 겹쳤습니다.
05공격 과정
5.1 공격 과정 요약
- 공격자가 비특권 사용자 네임스페이스(user namespace)를 만들어 그 안에서 CAP_NET_ADMIN을 얻고, 새 네트워크 네임스페이스에 nftables 테이블·체인·규칙을 등록할 권한을 확보합니다.
- netlink으로 규칙을 추가하며, '즉시 판정' 데이터 안에 하위 1바이트는 NF_DROP(0), 상위 16비트는 검증되지 않은 임의 값으로 인코딩된 32비트 원시 판정값을 실어 보냅니다.
- nft_immediate_init() → nft_data_init() → nft_verdict_init()이 이 값을 하위 1바이트만 확인한 채 그대로 struct nft_data의 verdict.code에 저장합니다(W1).
- 이 규칙이 매치되는 패킷을 흘려보내면 해당 훅에서 이 판정값이 반환되고, nf_hook_slow()가 하위 1바이트로 NF_DROP 분기를 타 kfree_skb_reason()으로 skb를 해제합니다(1차 해제).
- 이어서 nf_hook_slow()가 NF_DROP_GETERR(verdict)로 상위 16비트를 에러코드로 해석하는데, 공격자가 고른 상위 비트 값 때문에 이 계산 결과가 1(=NF_ACCEPT와 같은 정수)이 됩니다(W2).
- NF_HOOK() 매크로는 반환값이 1이면 '훅이 패킷을 통과시켰다'고 해석해 okfn()으로 정상 처리를 이어가며, 이미 해제된 skb를 다시 정상 패킷처럼 건드립니다 — 이 시점에서 use-after-free, 그리고 그 경로가 skb를 다시 해제하면 double-free가 성립합니다.
- 해제된 메모리 조각을 원하는 커널 객체로 재활용(힙 그루밍)해 신뢰성 있는 임의 읽기/쓰기 프리미티브까지 확장하면, 최종적으로 root 권한을 얻는 로컬 권한상승에 도달합니다 — 이 마지막 확장 단계(주소 유출·페이지테이블 조작 등)의 구체적 절차는 이 글에서 다루지 않습니다.
5.2 상세 공격 과정
예를 들어 공격자가 판정값의 하위 1바이트를 NF_DROP(0)으로, 상위 16비트를 0xffff로 채운 32비트 값(플레이스홀더 — 실제로는 0xffff0000 형태)을 규칙에 넣는다고 가정합니다. nft_verdict_init()의 검증(& NF_VERDICT_MASK)은 하위 1바이트만 보므로 이 값을 그대로 통과시킵니다.
이 규칙에 매치되는 패킷이 지나가면 nf_hook_slow()가 NF_DROP 분기로 들어가 kfree_skb_reason()을 호출합니다 — 이 시점에 skb는 이미 해제됩니다. 그다음 줄의 NF_DROP_GETERR(verdict)는 -(verdict >> 16)을 계산하는데, 상위 16비트가 0xffff(부호 있는 정수로는 -1)이므로 -(-1) = 1이 되어 nf_hook_slow()는 1을 반환합니다.
이 '1'이라는 숫자는 NF_DROP_GETERR() 입장에서는 그냥 계산된 에러코드지만, 이 함수를 호출한 NF_HOOK() 매크로 입장에서는 완전히 다른 뜻입니다 — NF_HOOK()의 코드는 if (ret == 1) ret = okfn(net, sk, skb);로, 반환값이 정확히 1이면 '이 훅이 패킷을 받아들였으니(NF_ACCEPT) 다음 정상 처리 함수를 실행하라'고 해석합니다. 그 결과 이미 해제된 skb가 okfn() 경로로 다시 흘러 들어가 사용후해제 상태에서 계속 처리되고, 그 경로가 skb를 다시 해제하는 지점에서 더블프리가 성립합니다.
여기서부터 안정적인 root 권한 획득까지 이어지는 구체적 힙 그루밍·주소 유출·페이지테이블 조작 절차는 원 발견자의 공개 기술 블로그가 상세히 다루고 있으나, 이 글은 그 절차를 재현하거나 요약하지 않습니다(무해화). 실제 동작하는 익스플로잇 코드나 트리거 값 조합은 이 글에 싣지 않습니다.
5.3 미흡점 ↔ 공격 흐름 매핑
| 미흡점(§4) | 발화 지점(§5.2) | 여는 프리미티브 | → 다음으로 잇는 것 |
|---|---|---|---|
| W1 — 상위 16비트 미검증 | nft_verdict_init() 저장 시점(§8.2 2~3단계) | 검증 안 된 원시 32비트 값이 verdict.code로 확정 | nf_hook_slow()가 이 값을 그대로 신뢰 |
| W2 — 상위 비트=에러코드 계약과 ret==1=통과 계약의 충돌 | nf_hook_slow()의 NF_DROP_GETERR() 계산(§8.2 4~5단계) | 해제 후 계산값이 NF_ACCEPT(1)와 정수적으로 충돌 | NF_HOOK()이 '통과'로 오판, okfn() 재진입 |
| (귀결) 사용후해제/더블프리 | okfn() 경로의 skb 재사용(§8.2 6단계) | 이미 해제된 메모리가 정상 객체로 재해석·재해제 | 힙 그루밍을 거친 임의 R/W 프리미티브 → 로컬 권한상승 |
06취약점 패치
왜§7 W1에서 지목한 자리 — 하위 1바이트만 검증하고 상위 16비트를 포함한 원본 값을 그대로 저장하는 로직 — 을 직접 겨냥한 수정입니다. 커밋 메시지는 이 취약한 로직을 만든 2014년 커밋(e0abdadcc6e1)을 명시적으로 지목하며, core.c:nf_hook_slow()가 'NF_DROP 판정의 상위 16비트에 유효한 에러코드가 들어 있다고 가정한다'고 정확히 짚습니다.
어떻게패치는 새 검증 로직을 추가한 게 아니라, 2014년 이전의 엄격한 switch (data->verdict.code) 구조를 그대로 복원했습니다 — NF_ACCEPT/NF_DROP/NF_QUEUE는 하위 비트 마스킹 없이 정확히 그 값(상위 16비트가 0인 경우)일 때만 통과하고, 스위치문 끝에 default: return -EINVAL;을 추가해 그 외의 모든 값(상위 비트가 채워진 값 포함)을 거부합니다. 즉 '상위 16비트가 0이 아닌 판정값은 애초에 nft_tables에 들어올 수 없다'로 계약을 되돌린 것입니다. 커밋 메시지는 "NF_QUEUE는 nftables가 쓰지 않는다(queue 규칙은 별도의 nft_queue 표현식을 쓴다)"고 덧붙이며, 훗날 정말 에러코드를 사용자에게 열어줘야 한다면 NF_DROP_GETERR()로 err <= 0을 확인하는 별도 검증이 필요하다고 명시합니다.
비고패치가 하는 일은 새 방어선을 세우는 게 아니라 '2014년에 왜 이 검증을 느슨하게 했는지 모르겠다(Its not clear to me why this commit was made)'는 커밋 메시지 그대로, 원래 있던 엄격한 검증으로 되돌아가는 것뿐입니다 — 10년 전 최적화 혹은 편의를 위한 변경이 검증되지 않은 채 남아 있었다는 뜻입니다.
07대응방안
근본 해결은 커널 업그레이드입니다. mainline 기준 커밋 f342de4e2f33e0e39165d8639387aa6c19dff660을 포함한 버전(사실상 v6.8 이상), stable/longterm 라인은 §영향받는 버전 표의 5.15.149/6.1.76/6.6.15/6.7.3 이상, 그 외 배포판(Ubuntu·Debian·RHEL 등)은 각 벤더 보안공지가 지목하는 패치 버전 이상으로 올립니다.
- 즉시 업그레이드가 어렵다면
kernel.unprivileged_userns_clone=0(또는 배포판에 따라user.max_user_namespaces=0)으로 비특권 사용자 네임스페이스 생성을 막는 것이 가장 널리 권고되는 임시 차단선입니다 — 이 취약점의 실전 트리거 경로 대부분이 사용자 네임스페이스로CAP_NET_ADMIN을 얻는 것을 전제로 하기 때문입니다. 다만 컨테이너 런타임(예: 로그인 없는 rootless 컨테이너)이 사용자 네임스페이스에 의존한다면 이 조치가 해당 기능을 깨뜨릴 수 있어, 환경별 영향도를 먼저 확인해야 합니다. nf_tables를 쓰지 않는 시스템이라면 커널 모듈 로딩 자체를 막는 것도 유효한 차선입니다(/etc/modprobe.d/에nf_tables블랙리스트 등록). 다만 최신 배포판 다수가 기본 방화벽 도구(nftables/firewalld)로 이 모듈에 이미 의존하므로, 실제로 미사용인지 먼저 확인해야 합니다.- 컨테이너·멀티테넌트 환경에서는 컨테이너에 부여하는 리눅스 capability에서
CAP_NET_ADMIN을 기본적으로 제거하거나, seccomp/AppArmor 프로파일로unshare(CLONE_NEWUSER)·unshare(CLONE_NEWNET)계열 시스템콜을 제한하는 것이 공격 표면을 줄이는 보조 수단입니다. - 탐지 관점. 짧은 시간 안에 반복적으로 사용자 네임스페이스를 생성하고(
unshare·clone(CLONE_NEWUSER|CLONE_NEWNET)) 직후 nftables 규칙을 반복 등록/삭제하는 프로세스 행위 패턴(auditd·eBPF 기반 EDR로 관찰 가능)을 이상 신호로 봅니다. 커널 패닉이나 KASAN/slab 무결성 경고, 또는 LKRG 같은 런타임 커널 무결성 도구가 보고하는 이상 종료도 (사후) 관찰 대상입니다 — 다만 이런 신호는 사후 탐지에 가깝고, 예방은 결국 버전 관리와 네임스페이스 제한으로 귀결됩니다.
08왜 이게 반복되는가
이 취약점이 흥미로운 이유는 코드가 복잡해서가 아니라, 정확히 반대이기 때문입니다. 검증 로직 자체는 몇 줄짜리 스위치문이고, 패치도 '10년 전 상태로 되돌리기'가 전부입니다. 위험한 건 한 정수 안에 서로 다른 두 가지 의미(무엇을 할지 + 부가 정보)를 겹쳐 담는 인코딩에서, 그 절반만 검증하면 나머지 절반이 다른 계약과 충돌할 수 있다는 사실이었습니다. 저희가 Jenkins CLI 임의 파일 읽기(CVE-2024-23897)에서 본 것과 결이 비슷합니다 — 그때는 '로컬 신뢰 전제로 만들어진 라이브러리 기본값'이 네트워크 노출 컨텍스트로 옮겨지며 다시 검토되지 않았고, 여기서는 '두 계층이 같은 정수 공간을 공유한다'는 사실이 입력 검증 설계에 반영되지 않았습니다. V8 Maglev 힙 손상(CVE-2024-0517)에서도 컴파일러가 자신이 통제하지 못하는 다른 서브시스템(GC)의 규칙을 몰랐다는 같은 계열의 이야기를 다뤘습니다 — 세 사건 모두 개별 코드는 각자 정상인데, 그 코드들이 공유하는 경계에서 전제가 어긋났습니다.
패치완전성 관점에서 이 사건은 비교적 깔끔한 사례입니다. 패치는 새 검증을 추가한 게 아니라, 검증을 느슨하게 만든 2014년 커밋 전체를 되돌렸습니다 — 원인 지점 하나를 정확히 겨냥한 수정이고, 커밋 메시지도 '왜 이 커밋이 만들어졌는지 모르겠다'며 원래 검증이 불필요하게 약화됐었다는 점을 스스로 인정합니다. 다만 이 CVE가 남기는 진짜 질문은 코드가 아니라 시간입니다 — 검증을 느슨하게 만든 변경이 10년간 아무도 문제 삼지 않았고, 2024년 1월에 조용히 패치된 뒤 2024년 5월 CISA KEV, 2025년 10월 랜섬웨어 실사용까지 갔습니다. '오래된 코드라서 안전하다'는 가정 자체가 이 취약점의 수명을 늘린 셈입니다.
이 한 틈은 고쳐졌지만, '한 값에 두 가지 의미를 겹쳐 담을 때 그중 절반만 검증하지 않았는가'라는 질문은 다른 서브시스템, 다른 프로토콜에서 또 나옵니다 — 그래서 저희는 이 커밋 하나가 아니라 이 질문을 남깁니다.
09결론
리눅스 커널 nf_tables의 nft_verdict_init()이 판정값의 상위 16비트를 검증하지 않은 채 저장한 것이 근본원인입니다. 이 검증되지 않은 값이 nf_hook_slow()의 에러코드 계산과 NF_HOOK()의 '통과' 판정 계약을 우연히 충돌시켜, 이미 해제된 네트워크 버퍼를 다시 처리하는 더블프리가 성립합니다. CVSS 7.8(High), CWE-416이며 2014년에 들어간 코드가 2024년 1월에야 되돌려졌습니다. 패치는 원인 지점을 정확히 되돌린 것으로 확인되지만, 10년간 방치됐다가 CISA KEV 등재·랜섬웨어 실사용까지 이어진 시간 자체가 이 사건의 핵심 교훈입니다.
이 CVE의 표준판(요약본)은 아직 별도로 작성되지 않았습니다.
참고 자료
이 글의 코드 발췌(nf_tables_api.c의 nft_verdict_init()·nft_data_init(), core.c의 nf_hook_slow(), include/linux/netfilter.h의 NF_DROP_GETERR()·NF_HOOK())는 torvalds/linux 오픈소스를 패치 커밋(f342de4e2f33e0e39165d8639387aa6c19dff660)의 부모 커밋(b462579b2b86a8f5230543cadd3a4836be27baf7, 패치 전 상태) 및 패치 커밋 자체(패치 후 상태) 기준으로 raw.githubusercontent.com에서 직접 받아 파일·라인 단위로 byte-level 대조했습니다. 패치 diff는 GitHub의 .patch 원문을 그대로 받아 확인했습니다. 검증값이 '더블프리를 유발하는 구체적 원인'(NF_DROP_GETERR가 verdict 상위 16비트를 부호 있는 정수로 해석해 우연히 1(NF_ACCEPT와 동일한 정수)이 될 수 있고, 그 값을 NF_HOOK() 매크로가 '훅이 패킷을 통과시켰다'는 신호로 오판한다는 연쇄)은 이 세 파일의 실제 코드를 직접 대조해 저희가 계산·재구성한 것이며, 이 인과 서술 자체는 1차 소스(코드)로 확인됩니다. 다만 이 더블프리를 임의 물리 메모리 읽기/쓰기 프리미티브로 확장하는 후반부 익스플로잇 기법(더러운 페이지테이블 기법 등)은 원 발견자의 공개 기술 블로그(pwning.tech)와 NVD·벤더 권고문·CISA 보도자료를 교차 확인한 2차 분석이며, 저희가 익스플로잇 코드 자체를 직접 실행·재현하지는 않았습니다 — 그 한계는 §패치 완전성 평가·본문 배지에 명시했습니다.
- Notselwyn(pwning.tech) — "Flipping Pages: An analysis of a new Linux vulnerability in nf_tables and hardened exploitation techniques" · 이 글이 재구성한 원 분석(발견자 본인의 기술 블로그), 익스플로잇 기법 개요의 출처
- oss-security 메일링리스트 — CVE-2024-1086 공개 공지 (2024-04-10) · 배포판별 패치 지연 논의 근거
- NVD — CVE-2024-1086 상세 · CVSS 7.8, CWE-416, 발행일 2024-01-31
- Linux 커널 오픈소스 — 패치 커밋 f342de4e2 · nft_verdict_init() 검증 복원 diff, Florian Westphal
- Ubuntu Security Notices — CVE-2024-1086 · 배포판별 패치 버전, 완화책(kernel.unprivileged_userns_clone) 근거
- CISA — Known Exploited Vulnerabilities Catalog 등재 공지 (2024-05-30)
- Sysdig — CVE-2024-1086 랜섬웨어 실사용 탐지 분석 · 2025-10-31 실사용 확인 근거
- infiltr8 Lab, 「인증이 필요 없던 명령이, 파일을 읽어내는 통로가 됐다」(Jenkins CLI, CVE-2024-23897) — 같은 계열(경계에서 어긋난 전제) 논평 연결
- infiltr8 Lab, 「super()를 부르는 사이, 컴파일러가 경계를 놓쳤다」(V8 Maglev, CVE-2024-0517) — 같은 계열 논평 연결