Upgrade to Pro — share decks privately, control downloads, hide ads and more …

高橋メソッドでCVE解説シリーズ 〜CVE-2026-99999〜

Avatar for Hakkadaikon Hakkadaikon
November 02, 2026

高橋メソッドでCVE解説シリーズ 〜CVE-2026-99999〜

高橋メソッドでCVEを解説する (CVE-2026-99999)
opus5.5で生成

Avatar for Hakkadaikon

Hakkadaikon

November 02, 2026

More Decks by Hakkadaikon

Other Decks in Technology

Transcript

  1. 公式の記述(CVE レコード) ipv4: validate checksum_start before completing checksum If a

    packet with bad checksum metadata gets into the ipv4 stack, skb_checksum_help can corrupt the network header and cause a bunch of mischief. チェックサムのメタデータが不正なパケットが IPv4 の処理に入ると skb_checksum_help() がネットワークヘッダ(IP ヘッダ)を壊す Linux カーネルの CVE は、修正コミットのメッセージがそのまま説明文になる 出典: cve.org / NVD CVE-2026-99999(CNA: Linux)
  2. 深刻度の評価(2026-11-03 時点) 評価元 Linux(CNA) CISA(SSVC) NVD 評価 CVSS 3.1: 9.3

    Critical Exploitation: none Automatable: no Technical Impact: total 独自スコアなし 備考 AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H 悪用は未確認。自動化しにくいが、影響は全面的 状態は Received(NVD 側の分析はまだ) AV:L(ローカル)なのに 9.3。効いているのは S:C(スコープ変更) 仮想マシンの中から、外側のホストに影響が及ぶという意味 出典: cve.org CVE-2026-99999 / NVD API 2.0
  3. ① IPv4 ヘッダ(オプションなしで 20 バイト) 0 1 2 3 0

    1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-------+-------+---------------+-------------------------------+ |Version| IHL | TOS | Total Length | 0 +-------+-------+---------------+-----+-------------------------+ | Identification |Flags| Fragment Offset | 4 +---------------+---------------+-----+-------------------------+ | TTL | Protocol | Header Checksum | 8 +---------------+---------------+-------------------------------+ | Source Address | 12 +---------------------------------------------------------------+ | Destination Address | 16 +---------------------------------------------------------------+ | Options (IHL > 5 ) | 20 のときだけ IHL = ヘッダ長を 4 バイト単位で表す 4 ビット。5 なら 20 バイト、最大 15 で 60 バイト 受け取った側は IHL を見て「どこまでがヘッダか」を決める RFC 791 Section 3.1
  4. ② TCP/UDP のチェックサムは「後で計算」できる struct sk_buff { /* ... */ __u16

    csum_start; __u16 csum_offset; /* ... */ }; カーネルのパケット 1 個 (抜粋) */ /* ここから末尾までを足し合わせ */ /* csum_start からこの位置に結果を書く */ /* 送信時、チェックサムの計算を NIC にまかせられる(オフロード) 「まだ計算していない」印が ip_summed = CHECKSUM_PARTIAL どこから計算して、どこに書くかを csum_start と csum_offset で持つ NIC がオフロードできないときは、カーネルが skb_checksum_help() で計算して書く Linux include/linux/skbuff.h:1044-1045, 166-182
  5. ③ sk_buff は「バッファ + 位置の数字」 skb->head skb->data | | v

    v +----- headroom ----------+--- Ethernet ---+--- IPv4 ---+--- TCP/UDP ---+-- payload --+ ^ ^ network_header csum_start ( ) 正しい場合 、 csum_start などはどれも head からのバイト位置 正しいパケットでは csum_start は IP ヘッダより後ろ(TCP/UDP ヘッダの先頭)を指す ただし、ただの数字なのでIP ヘッダの中を指すこともできる network_header
  6. ④ 仮想マシンのパケットはこう流れる ゲスト OS (virtio-net ドラ イバ) virtio_net_hdr + Ethernet

    フレーム VMM ホストのプロセ ス) ( デバイス (ホストのカーネ ル) TAP write(tap_fd, ...) ホストの IPv4 処 理 (ルーティング・ 転送) 物理 NIC ゲストはフレームの前に virtio_net_hdr を付ける。ここに csum_start / csum_offset が入って いる VMM はそれを TAP にそのまま書く( IFF_VNET_HDR )。カーネルが skb に反映する つまり ゲストが決めた数字が、ホストのカーネルの skb に入る Linux drivers/net/tun.c / include/linux/virtio_net.h、CVE レコードの CVSS シナリオ
  7. ⑤ 大きすぎるパケットは分割して送る 元のパケット(MTU を超える) +--------+-------------------------------------------+ | IP hdr | data

    | +--------+-------------------------------------------+ 分割後(各片に IP ヘッダをコピーして付ける) +--------+--------------+ +--------+--------------+ +--------+-------+ | IP hdr | data [0..n) | | IP hdr | data [n..2n) | | IP hdr | | +--------+--------------+ +--------+--------------+ +--------+-------+ 残り 送り先のリンクの MTU より大きいと、カーネルが ip_do_fragment() で分割する 分割の前に、残っているチェックサムを skb_checksum_help() で確定させる 各片に付けるヘッダの長さは IHL から計算する
  8. 場面 1: TAP は skb の先頭を Ethernet にして渡す case IFF_TAP:

    if (!pskb_may_pull(skb, ETH_HLEN)) { /* ... */ goto drop; } skb_set_network_header(skb, ETH_HLEN); break; } if (tun_vnet_hdr_tnl_to_skb(tun->flags, features, skb, &hdr)) { /* ... */ goto free_skb; } ゲストから来たフレームの virtio_net_hdr を skb に反映する この時点の skb->data は Ethernet ヘッダの先頭 Linux(修正直前 f8c8bd159a)drivers/net/tun.c:1928-1942
  9. 確認は「先頭から 20 バイト以上」だけ unsigned int nh_min_len = sizeof(struct iphdr); /*

    = 20 */ /* ... */ if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { u32 start = __virtio16_to_cpu(little_endian, hdr->csum_start); u32 off = __virtio16_to_cpu(little_endian, hdr->csum_offset); /* ... */ if (!skb_partial_csum_set(skb, start, off)) return -EINVAL; if (skb_transport_offset(skb) < nh_min_len) return -EINVAL; が skb->data から 20 バイト未満なら捨てる。確認はこれだけ TAP では skb->data が Ethernet の先頭。Ethernet ヘッダは 14 バイト 20 − 14 = 6。IP ヘッダの 6 バイト目以降を指す値が通ってしまう csum_start Linux(f8c8bd159a)include/linux/virtio_net.h:55, 97-108
  10. skb_partial_csum_set も「範囲内か」だけを見る bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)

    { u32 csum_end = (u32)start + (u32)off + sizeof(__sum16); u32 csum_start = skb_headroom(skb) + (u32)start; if (unlikely(csum_start >= U16_MAX || csum_end > skb_headlen(skb))) { /* ... */ return false; } skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = csum_start; skb->csum_offset = off; /* ... */ 見ているのは「書く 2 バイトがバッファに収まるか」 IP ヘッダとの位置関係は見ていない(この関数はプロトコルを知らない) Linux(f8c8bd159a)net/core/skbuff.c:5829-5844
  11. IP ヘッダの中で「書ける」範囲(TAP で VLAN なしの場合) offset from frame start 0

    14 20 34 +-------------+-----+-------------+--------- ... | Ethernet | IPv4 header | TCP/UDP +-------------+-----+-------------+--------- ... | |<-- here -->| | | csum_start | IP offset: 0 6 20 Flags/Fragment Offset、TTL、Protocol、Header Checksum、送信元・宛先アドレスが範囲に入る 書かれる 2 バイトの値は csum_start から末尾までの1 の補数和。足し合わせるバイトはゲストが決 める CVE レコードは「検証済みの IHL が書き換わった」と述べている(具体的な配置は一次情報に記載なし) 上の 2 枚のコードから読める範囲。IHL の記述は CVE レコードの CVSS シナリオ
  12. 場面 2: 分割の前に skb_checksum_help を呼ぶ /* for offloaded checksums cleanup

    checksum before fragmentation */ if (skb->ip_summed == CHECKSUM_PARTIAL && (err = skb_checksum_help(skb))) goto fail; /* * Point into the IP datagram header. */ iph = ip_hdr(skb); ゲストのパケットをホストが別のリンクへ転送し、MTU を超えると ip_do_fragment() に来る 最初にチェックサムを確定させる。このとき skb は IP ヘッダの検証を通過済み Linux(f8c8bd159a)net/ipv4/ip_output.c:773-783
  13. skb_checksum_help: 言われた場所に書く offset = skb_checksum_start_offset(skb); ret = -EINVAL; if (unlikely(offset

    >= skb_headlen(skb))) { /* WARN_ONCE ... */ goto out; } csum = skb_checksum(skb, offset, skb->len - offset, 0); offset += skb->csum_offset; if (unlikely(offset + sizeof(__sum16) > skb_headlen(skb))) { /* WARN_ONCE ... */ goto out; } /* ... */ *(__sum16 *)(skb->data + offset) = csum_fold(csum) ?: CSUM_MANGLED_0; ここでも見ているのは「バッファに収まるか」だけ skb->data はいま IP ヘッダの先頭。 offset が IP ヘッダの中ならヘッダを上書きする Linux(f8c8bd159a)net/core/dev.c:3643-3664(WARN 出力を省略)
  14. 「正しさの確認はスタックの仕事」と書いてある * * * * * * A driver may

    verify that the csum_start and csum_offset values are valid values given the length and offset of the packet, but it should not attempt to validate that the checksum refers to a legitimate transport layer checksum -- it is the purview of the stack to validate that csum_start and csum_offset are set correctly. と csum_offset が正しいかを確かめるのはネットワークスタックの責任 受け取り口(virtio_net_hdr の反映)も、書き込み口( skb_checksum_help )も、長さしか見ていなかっ た csum_start Linux include/linux/skbuff.h:171-176
  15. 場面 3: 書き換わった IHL でヘッダ長を決める hlen = iph->ihl * 4;

    if (mtu < hlen + 8) { err = -EMSGSIZE; goto fail; } mtu = mtu - hlen; /* Size of data space */ skb_copy_from_linear_data(skb, skb_network_header(skb2), state->hlen); は書き換えた後の IHL から計算される 各片を作る ip_frag_next() は元の skb から hlen バイトをヘッダとしてコピーする 本来 20 バイトのヘッダが、たとえば 60 バイト扱いになれば、後ろの 40 バイトも「ヘッダ」として送 られる hlen Linux(f8c8bd159a)net/ipv4/ip_output.c:792-797, 719
  16. C で書くとこういう関数 はもう検証済みの パケット。 は外から来た数字 /* buf IP off */

    int complete_checksum(uint8_t *buf, size_t len, uint16_t start, uint16_t off) { if (start + off + 2 > len) /* ← */ return -EINVAL; uint16_t sum = fold(sum_bytes(buf + start, len - start)); memcpy(buf + start + off, &sum, 2); /* IP */ return 0; } 範囲チェックだけ ヘッダの中にも書ける 正しくは、 ヘッダより後ろでなければ書かない */ /* IP if (start < ip_hdr_len(buf)) return -EINVAL; 説明用の擬似コード。カーネルの実コードではない
  17. CVE レコードが述べる攻撃の筋書き 悪意あるゲスト(microVM) ホストのカーネル VMM 細工した virtio_net_hdr + フレーム そのまま転送)

    write(tap_fd)( tun_get_user() → IPv4 の処理へ ip_do_fragment() → skb_checksum_help() IP ヘッダを上書き 書き換わった IHL で分割 ホストのメモリを含むパケット 出典: CVE レコードの CVSS シナリオ(AV:L / AC:L / C:H)
  18. CVE レコードに書かれた「実証済み」の影響 観点 機密性 完全性 可用性 内容 分割で作るパケットの IPv4 オプションに、ホストのメモリがコピーされて送られた

    細工したパケットで、ホストの管理者権限でコマンドを実行できた 物理ホストをクラッシュさせ、上のサンドボックスを全部止めた いずれも CNA(Linux)が CVSS の根拠として CVE レコードに書いたもの 手順の詳細は公開の一次情報には書かれていない 出典: cve.org CVE-2026-99999 CVSS シナリオ C:H / I:H / A:H
  19. なぜ「PR:N」なのか PR:N - In the microVM scenario, a tenant workload

    able to transmit through its assigned virtio-net interface can supply the triggering traffic. It needs no host credentials, capability, or access to /dev/net/tun ホストの権限は何もいらない。自分の仮想 NIC からパケットを出せればよい VMM はゲストの出力を、もともと持っている TAP にそのまま流すだけ レースもタイミングもいらず、同じ手順を何度でも繰り返せる(AC:L) 出典: CVE レコードの CVSS シナリオ PR:N / AC:L
  20. 修正コミットの Fixes タグ Fixes: f43798c27684 ("tun: Allow GSO using virtio_net_hdr")

    Fixes: bfd5f4a3d605 ("packet: Add GSO/csum offload support.") : TAP/TUN に virtio_net_hdr を付けて書く経路。今回の筋書きの入口 packet : AF_PACKET ソケットに virtio_net_hdr を付けて送る経路 どちらも「外から来たチェックサムの位置情報」を skb に入れる tun Linux commit 6785011f8b16 のメッセージ
  21. 始まりは 2008 年 + if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { +

    if (!skb_partial_csum_set(skb, gso.csum_start, + gso.csum_offset)) { + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } TAP が virtio_net_hdr を受け付けるようになったコミット(Rusty Russell、2008-07-03) このころから「範囲に収まるか」だけを確かめていた 影響範囲は Linux 2.6.27 以降(CVE レコード) Linux commit f43798c27684 "tun: Allow GSO using virtio_net_hdr"
  22. 新しい関数: ip_check_csum_start static inline int ip_check_csum_start(const struct sk_buff *skb) {

    if (unlikely(skb->csum_start < skb->network_header + ip_hdrlen(skb))) return -EINVAL; return 0; } が IP ヘッダの終わりより前ならエラー csum_start も network_header も head からの位置なので、そのまま比べられる 書き込み位置は csum_start + csum_offset で、 csum_offset は符号なし。起点を見れば IP ヘッ ダには書けない csum_start Linux commit 6785011f8b16 include/net/ip.h
  23. ip_do_fragment の差分 /* for offloaded checksums cleanup checksum before fragmentation

    */ - if (skb->ip_summed == CHECKSUM_PARTIAL && (err = skb_checksum_help(skb))) goto fail; + if (skb->ip_summed == CHECKSUM_PARTIAL) { + err = ip_check_csum_start(skb); + if (!err) + err = skb_checksum_help(skb); + if (err) + goto fail; + } 確認してから書く。おかしければパケットごと捨てる Linux commit 6785011f8b16 net/ipv4/ip_output.c
  24. 同じ形の呼び出しをすべて直した ファイル net/ipv4/ip_output.c net/bridge/netfilter/nf_conntrack_bridge.c net/netfilter/nfnetlink_queue.c net/netfilter/xt_CHECKSUM.c net/xfrm/xfrm_output.c 場面 IPv4 の分割(今回の筋書き)

    ブリッジ上での分割 NFQUEUE でユーザ空間へ渡すとき iptables の CHECKSUM ターゲット IPsec の送信 いずれも IPv4 のパケットに対して skb_checksum_help() を呼ぶ場所 Linux commit 6785011f8b16 の変更ファイル一覧(6 files, +44 −12)
  25. コミットメッセージが認める「まだ途中」 We really shouldn’t allow such packets in, but as

    a defence in depth measure, let’s also check before we complete the checksum. A more complete validation at input is forthcoming, but needs more work. 本来は入口(virtio_net_hdr を受け取るところ)で止めるべき 今回の修正は出口(チェックサムを書く直前)での多層防御 入口でのきちんとした検証は「今後」とされている Linux commit 6785011f8b16 のメッセージ
  26. 修正コミット 項目 mainline 作者 レビュー 報告者 stable 内容 (作成 2026-10-07、netdev

    取り込み 2026-10-08) Michael S. Tsirkin Willem de Bruijn Paulos Yibelo(2026-09-22 に netdev へ報告) 5.10 / 5.15 / 6.1 / 6.6 / 6.12 / 6.18 / 7.2 へバックポート 6785011f8b16 Linux commit 6785011f8b16 と stable の各コミット(git.kernel.org)
  27. 修正済みバージョン 系列 5.10 5.15 6.1 6.6 6.12 6.18 7.2 mainline

    修正版 5.10.272 5.15.223 6.1.190 6.6.159 6.12.113 6.18.57 7.2.11 7.3 出典: CVE レコードの affected / NVD の CPE
  28. ① 更新と確認 いま動いているカーネル # uname -r ディストリビューションのカーネルを更新して再起動 系の例 # #

    (Debian/Ubuntu ) sudo apt update && sudo apt upgrade sudo reboot 上の表の修正版以上か、ディストリビューションの告知でこの CVE が直った版かを確かめる カーネルは再起動するまで新しくならない ディストリビューションは独自の版番号でバックポートすることがある
  29. ② 先に直すべきホスト 信用できないゲスト(他人のコード、マルチテナント)を仮想マシンで動かすホスト virtio-net + TAP でゲストのパケットをホストが転送する構成 ゲストのパケットがホストで分割されうる構成(転送先のリンクの MTU が小さい、など)

    信用できないプロセスに AF_PACKET( CAP_NET_RAW )を許しているホスト 逆に、信用できないコードを動かさないホストは相対的に後回しにできる(それでも更新はする) 入口は修正コミットの Fixes タグと CVE レコードのシナリオから整理
  30. 参考資料 CVE レコード: https://www.cve.org/CVERecord?id=CVE-2026-99999 NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-99999 修正コミット(mainline): https://git.kernel.org/stable/c/6785011f8b16abf40b1e9d8b2e332232b3e68822 stable の修正コミット:

    a464d950f31c / 0fab003ef469 / 94524b7b3269 / a65f69e0f38f / 32a8b0adaa18 / f3e835af6226 / c3e334647461 由来コミット: f43798c27684 "tun: Allow GSO using virtio_net_hdr"(2008) ソース(修正直前 f8c8bd159a): net/core/dev.c / net/core/skbuff.c / net/ipv4/ip_output.c / include/linux/virtio_net.h / drivers/net/tun.c / include/linux/skbuff.h RFC 791 Section 3.1(IPv4 ヘッダ) CVE レコードには報告者側の記事へのリンクもある(本資料では内容を使っていない)