
Linux 커널의 CVE-2026-98432 노출 여부를 탐지하고, IP 옵션을 포함한 인바운드 IPv4 SYN을 드롭하는 nftables 규칙을 적용하는 Ansible 역할 및 Python 스크립트입니다.
Linux 커널의 IPv4 TCP 코드에 존재하는 use-after-free 취약점
(CVE-2026-98432, CVSS 9.8):
들어오는 SYN에서 저장된 IPv4 옵션이 다른 CPU가 여전히 이를 읽어 SYNACK을
생성하는 동안 해제될 수 있는데, 이는 두 개의 소멸자 경로가 RCU 유예 기간을
기다리지 않고 단순한 kfree()로 이를 해제하기 때문입니다.
업스트림 수정은
e3f33b0a1d89
("ipv4: free inet_opt and ireq_opt after an RCU grace period")입니다.
2026-10-29 기준으로 어떤 Ubuntu 시리즈에도 수정된 커널이 릴리스되지 않았습니다. Ubuntu 트래커의 모든 영향받는 상태는 "work in progress" 또는 단순히 취약함으로 표시되며, 해당 페이지에는 완화 방법이 나열되어 있지 않습니다. 이 팩이 바로 그 완화책입니다.
두 경쟁 경로 모두 커널이 들어오는 SYN에서 저장한 IPv4 옵션인
ireq->ireq_opt를 읽습니다(tcp_v4_save_options()): SYNACK 생성은 이를
읽는 반면 자식 소켓 정리는 이를 해제할 수 있고, 요청 소켓 마이그레이션
(net.ipv4.tcp_migrate_req=1, 또는
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE 프로그램)은 동일한 옵션을 공유한 뒤
해제합니다. SYN이 IPv4 옵션을 포함하지 않으면 ireq_opt는 NULL입니다.
해제할 것도 읽을 것도 없습니다. 우리는 이에 의존하기 전에 수정 커밋을
읽었으며, 두 부분 모두 IPv4 옵션을 포함한 SYN에서 시작합니다.
따라서 이 팩은 IP 옵션을 포함한 인바운드 IPv4 TCP SYN을 드롭하는 nftables 규칙 하나를 설치하며, Docker, 기반 계층 또는 배포판의 규칙과 절대 충돌하지 않도록 자체 테이블에 배치합니다:
table inet samp_cve_2026_98432 {
chain prerouting {
type filter hook prerouting priority -300; policy accept;
ip hdrlength > 5 tcp flags & (syn | ack) == syn counter drop \
comment "CVE-2026-98432: drop inbound IPv4 SYN carrying IP options"
}
}
릴리스 전에 실제 커널에서 검증했다: 옵션을 포함한 IPv4 SYN은 카운터에 도달하여 드롭되고, 일반 SYN은 그대로 통과한다. TCP 트래픽은 IP 옵션을 거의 실어 나르지 않으며(여러 옵션 유형은 RFC 7126에 따라 많은 네트워크에서 당연하게도 드롭된다), 따라서 이 규칙의 운영 비용은 거의 없다.
pve 커널을 제공하며,
Ubuntu 페이지는 이를 다루지 않는다)은 unknown으로 보고된다: 해당
배포판의 권고를 읽어야 한다. 규칙 자체는 모든 Linux 호스트를 보호할
수 있지만, Ubuntu 전용인 것은 판정 테이블이다.규칙은 커널 메모리에 존재하며 디스크에 지속되지 않는다. 완화
패키지는 지속성 경로(유닛, cron)에 기록해서는 안 되며, nftables 포함
파일은 배포판의 nftables 서비스가 활성화된 곳에서만 로드되는데, 이런
환경에서는 그것이 일률적이지 않다. 따라서 재부팅 후 탐지는 규칙이
없다고 읽으며(모든 결과 줄에 rule_live=0, persistent=0), 다음
적용이 이를 복원한다. 재부팅 후 적용을 다시 실행하는 것이 운영
모델이며, 결과 줄은 결코 그와 다르게 주장하지 않는다.
읽기 전용이며, 스스로 정리하는 비공개 임시 디렉터리에 스테이징하고 다음을 보고한다:
uname -r, 해당 패키지 버전으로 매핑됨)을
릴리스 및 플레이버별 Ubuntu의 수정 버전과 대조한다. 해당 박스의
릴리스에 대한 수정이 릴리스되지 않은 경우: no_fix. 수정된 커널이
릴리스되었으나 실행 중이 아닌 경우: vulnerable, 설치되어 재부팅을
기다리는 경우 restart_needed=1(설치된 수정은 결코 fixed로
보고되지 않는다).workaround: true와 함께 fixed; 실행 중인
커널이 수정되면 단순히 fixed.not_affected, 테이블이 다루지
않는 릴리스 및 배포판에 대해서는 unknown.0 정상(fixed, not_affected), 2 노출 또는 미결
(no_fix, vulnerable, unknown: 결코 정상 아님), 1 실행 자체
실패, 130 중단됨.syn_options_mitigation/files/samp_common.py는
samp-common 1.0.4의
바이트 단위 복사본이다(dpkg, rpm 및 apk 버전 비교, 결과 줄, 설정
로딩). 그 sha256은 samp.yaml에 있으며, 복사본이 다르면
tests/test_samp_pack.py가 실패한다. 여기서는 절대 편집하지 말고,
samp-common의 scripts/vendor.py로 다시 벤더링하라.samp/v<version>(이번 것은
samp/v1.0.1)이며, 하드웨어 기반 키로 서명된다. 기반은 브랜치가
아니라 서명된 태그를 가져온다.스크립트 단계 하나, 결과 줄 하나. 실행 중인 커널이 수정되지 않은 동안
규칙을 로드하며, 수정된 커널이 실행되면 규칙과 그 테이블을 제거한다.
배포하는 스크립트, 라이브러리 및 설정(samp.yaml의 touches에
선언됨) 외에는 어떤 파일도 편집하지 않으므로, 결과 줄의 백업 목록은
비어 있다. 완화를 수동으로 제거하는 것은 명령 하나이다:
nft delete table inet samp_cve_2026_98432
ansible-playbook -i inventory play.yml # a play that runs the role
python3 syn_options_mitigator.py --config syn_options_config.yaml # detect
sudo python3 syn_options_mitigator.py --config syn_options_config.yaml --apply
Ubuntu가 수정된 커널을 릴리스하면, 트래커의 버전을 역할 기본값의 syn_options_fixed_versions에 붙여넣고(작업 중인 버전은 이미 주석으로 들어 있음) 해당 변경을 다음 서명된 태그로 릴리스합니다. 그러면 탐지가 해당 버전에 대해 vulnerable/restart_needed를 보고하기 시작하고, 적용 시 수정된 커널로 재부팅되는 박스에서 규칙이 제거됩니다.
python3 -m pytest tests/
단위 테스트는 모든 외부 명령을 가짜로 대체합니다. 계약 테스트
(tests/test_samp_pack.py, samp-common의 것)는 매니페스트를
역할과 대조하여 검증하고, 임시 디렉터리에서 실제 탐지를 실행하며, apply
엔트리가 단일 결과 줄 및 needrestart 규칙을 준수하는지 확인합니다.
MIT. LICENSE를 참조하세요.
PR과 개선 사항을 환영합니다 -> 포크한 후 병합 요청을 제출해 주세요.