
IPv4のIPオプション付きTCP SYNをホストが破棄するかどうかを確認します(CVE-2026-98432の緩和策)
ホストがIPオプション付きのIPv4 TCP SYNを破棄するかを検証します。これはCVE-2026-98432(LinuxカーネルのIPv4 TCPハンドシェイクにおけるuse-after-free、CVSS 9.8、修正済みカーネルがリリースされるまでの間)に対する推奨ネットワーク緩和策です。
各ポートに対して、通常のSYNとIPv4 Record Route付きのSYNを送信し、応答を比較します。無害です。何も悪用せず、ハンドシェイクを開始する(そして完了しない)だけです。
このテストが測定するもの: IPオプション付きのSYNがターゲットに到達し、受け入れられるかどうかです。カーネルのバージョンは読み取りません。修正済みのカーネルでもIPオプションを引き続き受け入れるため、
EXPOSTOと出力されます。その場合のEXPOSTOは単に「IPオプションのフィルタがない」ことを意味し、修正の有無はuname -rで確認できます。
./verifica-cve-2026-98432.sh <host-ou-ip> [porta ...] # porta padrão: 443
./verifica-cve-2026-98432.sh exemplo.com 443 80 22
スクリプトなしで、イメージから直接(ターゲットはIPv4である必要があります):
docker run --rm --network host --cap-add NET_RAW \
ghcr.io/marcelofmatos/cve-2026-98432-check:latest 203.0.113.10 443 22
| 出力 | Exit | 意味 |
|---|---|---|
MITIGADO | 0 | 通常の SYN は受理され、IP options 付き SYN は破棄される — かつ経路上のどのホップも IP options をフィルタしていない |
EXPOSTO | 1 | IP options 付き SYN も受理された: 経路上にもターゲットにもフィルタがない |
INCONCLUSIVO | 2 | ポートが閉じている (RST) かフィルタされている — 開いているポートを使用してください |
SEM HOST | 2 | ターゲットがローカルネットワーク上にあり、ARP に誰も応答しない: IP が空いているかマシンの電源が切れている。TCP プローブは送信されない |
NÃO CONFIÁVEL | 3 | いずれかの経路ホップ (VPN、キャリア) がすでに IP options を破棄している。「破棄された」ことはターゲットについて何も証明しない: ネットワークの別の地点からテストしてください |
| — | 64 | 使用方法が誤っている、または IPv4 を持たない名前 |
複数ポートの場合、exit は最悪ケース: いずれかが EXPOSTO → 1。
詳細:
getent)。そのため VPN/内部 DNS の名前も機能する。prerouting に入る。CHECK_IMAGE (デフォルト ghcr.io/marcelofmatos/cve-2026-98432-check:latest)。ログの 1 行目
がバージョン: cve-2026-98432-check <versão> iniciando。多くの VPN やキャリアは IP options 付きのパケットを破棄する。そのような環境の背後からテストすると、
すべてのターゲットが緩和済みに見える。そのため検証ツールはテスト前に経路をホップごとに (TTL を
増やしながら) たどり、いずれかのホップが IP options を破棄していれば NÃO CONFIÁVEL を返す。
限界: ICMP に応答しないホップ上のフィルタは見逃される。テスト地点は緩和が適用されていないことが
既知のターゲットで検証すること: それは EXPOSTO と出力されなければならない。
脆弱な Linux は SYN-ACK で IP options を返す (Record Route 付き SYN は ihl=6 と RR オプションを
付けて戻ってくる)。mitigacao/ のルールがあるテスト地点では、この SYN-ACK は
prerouting で破棄される。それでも結果は正しい: scapy は netfilter の前にパケットソケットで
応答をキャプチャする。実測: ルールのあるホストからルールのないターゲットをテストしたところ、結果は
EXPOSTO でルールのカウンタが 1 増加した (SYN-ACK はキャプチャ後に破棄された)。テスト
ルールなしのターゲット (コンテナ) -> EXPOSTO がこのケースを CI でカバーしており、CI はルールなしと
ルールありのランナーでテストを実行する。
例外: br_netfilter を有効にしたテスト地点自身の bridge の背後のターゲット。 モジュールが
ロードされている場合 (net.bridge.bridge-nf-call-iptables = 1、Docker Swarm ホストや多くの
Proxmox のデフォルト)、IPv4 の prerouting は bridge 内でもキャプチャの前に実行される。この場合、
テスト地点のルールがその bridge 上のコンテナや VM の SYN-ACK を破棄し、結果は偽の MITIGADO
となる。通常の NIC (ネットワーク上の別マシン) 経由で到達するターゲットは影響を受けない。
br_netfilter のあるホストのコンテナや VM をテストするには、その外側のテスト地点、またはルールの
ないテスト地点を使用すること。テストはこの組み合わせを検出するとこのケースをスキップする (SKIP)。
ターゲットのポートが Docker により公開されている場合 (コンテナへの DNAT)、ターゲット自身がパケットを
転送するため、低い TTL のプローブに対して ICMP「TTL 超過」を返す。検証ツールはこの ICMP を送信元 IP
(ターゲットの IP) で認識し、経路ホップとして扱わない。バージョン 1.1.0 までは、リストの 1 番目の
ポートが転送されている場合、緩和済みのターゲットで偽の NÃO CONFIÁVEL を返していた。テスト
緩和済みターゲットで転送されるポート -> MITIGADO がこのケースをカバーしている。
mitigacao/ 内:
cve-2026-98432.nft: prerouting (priority -300) で ip hdrlength > 5 の TCP IPv4 を破棄する
テーブル ip cve_2026_98432。冪等。cve-2026-98432-mitigation.service: ネットワーク起動前にルールをロードする systemd unit。sudo install -m 644 mitigacao/cve-2026-98432.nft /etc/cve-2026-98432.nft
sudo install -m 644 mitigacao/cve-2026-98432-mitigation.service /etc/systemd/system/
echo 'net.mptcp.enabled = 0' | sudo tee /etc/sysctl.d/90-cve-2026-98432-mptcp.conf
sudo sysctl -w net.mptcp.enabled=0
sudo systemctl daemon-reload && sudo systemctl enable --now cve-2026-98432-mitigation
修正済みカーネルを置き換えるものではありません。ディストリビューションが公開次第、インストールして再起動してください。
flowchart LR
U["Operador"] --> L["verifica-cve-2026-98432.sh<br/>resolve o nome com getent<br/>escolhe a rede do container"]
L -->|"docker run --cap-add NET_RAW"| C["Container cve-2026-98432-check<br/>Alpine + Python + scapy"]
C -->|"rede host: alvo remoto"| N["Caminho de rede<br/>VPN, operadora, internet"]
C -->|"bridge: alvo é a própria máquina"| H["prerouting do host"]
N --> A["Alvo IPv4<br/>porta TCP"]
REG["GHCR<br/>ghcr.io/marcelofmatos/cve-2026-98432-check"] -.->|"docker pull"| C
flowchart TD
S["Início: alvo e portas"] --> AQ{"Alvo na rede local<br/>e sem resposta ARP?"}
AQ -->|"sim"| SH["SEM HOST — exit 2"]
AQ -->|"não"| H["TTL = 1, 2, 3 ... até o alvo<br/>SYN normal e SYN com RR"]
H --> HQ{"Algum salto responde ICMP ao normal<br/>e some com o RR?"}
HQ -->|"sim"| FL["caminho FILTRA no salto N"]
HQ -->|"não, chegou ao alvo, ICMP veio do próprio alvo<br/>ou 4 saltos mudos seguidos"| OK["nenhum filtro visto"]
FL --> P
OK --> P
P["Por porta: SYN normal"] --> PQ{"SYN-ACK?"}
PQ -->|"não"| INC["INCONCLUSIVO — exit 2"]
PQ -->|"sim"| R["SYN com RR"]
R --> RQ{"Alguma resposta?"}
RQ -->|"sim"| EXP["EXPOSTO — exit 1"]
RQ -->|"não, caminho filtra"| NC["NÃO CONFIÁVEL — exit 3"]
RQ -->|"não, caminho limpo"| MIT["MITIGADO — exit 0"]
docker build --build-arg APP_VERSION=dev -t cve-2026-98432-check:dev .
# ESPERADO_LOCAL=1 num host sem a regra nft; 0 num host com a regra aplicada
CHECK_IMAGE=cve-2026-98432-check:dev ESPERADO_LOCAL=1 VERSAO_ESPERADA=dev ./tests/testes.sh
テストはホスト上に一時的なリスナー(ポート18098)を起動し、Dockerのブリッジ上のコンテナからそれをテストします。CI(test.yml)では、ランナーが2回実行します:
ルールなし(EXPOSTO と出力される必要がある)とルール適用済み(MITIGADO と出力される必要がある)。
これにより、両方向で手法が検証されます。
手動ワークフロー release-and-build.yml(Actions →
Release and build → patch/minor/major):テストを実行し、SemVerタグとリリースを作成し、
イメージを x.y.z、x.y、x、latest のタグでGHCRに公開します。バージョンは
APP_VERSION を介してイメージに組み込まれます。
MIT