
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を構築するために
まだ読み取っている最中に解放される可能性があります。これは、2つのデストラクタ経路が
RCU grace periodを待たずに単純なkfree()でそれらを解放するためです。
アップストリームの修正は
e3f33b0a1d89
("ipv4: free inet_opt and ireq_opt after an RCU grace period")です。
2026-10-29時点で、どのUbuntuシリーズ向けにも修正済みカーネルはリリースされていません。 Ubuntuのトラッカー上の影響を受けるステータスはすべて「work in progress」または 単に脆弱と表示されており、ページには緩和策が記載されていません。このパックが その緩和策です。
競合する両方の経路はireq->ireq_optを読み取ります。これはカーネルが受信したSYNから
保存したIPv4オプションです(tcp_v4_save_options()): SYNACKの構築はそれらを読み取り、
その間に子ソケットの破棄がそれらを解放する可能性があり、またrequest-socketの
マイグレーション(net.ipv4.tcp_migrate_req=1、または
BPF_SK_REUSEPORT_SELECT_OR_MIGRATEプログラム)は同じオプションを共有した後に
解放します。SYNがIPv4オプションを運ばない場合、ireq_optはNULLです: 解放すべきものも
読み取るべきものもありません。私たちはこれに依拠する前に修正コミットを読みました;
両方の部分はIPv4オプションを運んだSYNから始まります。
そこでこのパックは、IPオプションを運ぶインバウンドIPv4 TCP SYNをドロップする nftablesルールを1つインストールします。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)、次の apply で復元される。再起動後に apply を再実行することが運用モデルであり、結果行がそれ以外を主張することは決してない。
読み取り専用で、自身をプライベートな一時ディレクトリにステージングし、それを削除した上で、以下を報告する:
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)であり、ハードウェアベースのキーで署名されている。基盤はブランチではなく署名済みタグをインポートする。1 つのスクリプトステップ、1 つの結果行。実行中のカーネルが未修正の間はルールをロードし、修正済みカーネルが実行されるとルールとそのテーブルを削除する。デプロイするスクリプト、ライブラリ、設定(samp.yaml の touches で宣言)以外のファイルは編集しないため、結果行のバックアップリストは空である。緩和策を手動で削除するのは 1 つのコマンドで済む:
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 を報告し始め、apply は修正済みカーネルで再起動したマシンからルールを削除します。
python3 -m pytest tests/
ユニットテストはすべての外部コマンドをフェイク化します。コントラクトテスト
(tests/test_samp_pack.py、samp-common のもの)はマニフェストを
ロールに対して検証し、スクラッチディレクトリから実際の検出を実行し、apply
エントリを単一結果行および needrestart のルールに照らして検証します。
MIT。LICENSE を参照してください。
PR や改善を歓迎します -> フォークしてマージリクエストを送信してください。