
CVE-2026-46331(通称 「pedit COW」)は、トラフィック制御サブシステムにおけるLinuxカーネルのローカル権限昇格の欠陥です。権限のないユーザー(権限のないネットワーク名前空間内)が act_pedit(パケットエディタ)フィルタを構成して、ページキャッシュへの部分的なコピーオンライト(COW)書き込みを引き起こす可能性があります。実際には、カーネルは攻撃者が制御するデータをファイルのインメモリイメージにページをプライベートとしてマークせずに書き込み、そのファイルのキャッシュされたコピーを破壊します。重要な点として、このエクスプロイトに必要なのはCAP_NET_ADMIN(ユーザー名前空間で取得可能)だけであり、ディスク上のファイルは変更しません。実際には、packet_edit_meme と呼ばれる実証用の概念実証(PoC)が2026年6月17日に公開され、setuidバイナリ(例:/bin/su)のページキャッシュイメージを上書きしてルートシェルを起動する方法を示しました。この脆弱性は、tcf_pedit_act() における誤ったCOW範囲の計算に起因し、上流では(2026年6月4日に)書き込み可能領域のチェックをキーごとのループ内に移動することで修正されています。
act_pedit を備えたLinuxカーネル(約v5.18から7.1-rc6)。パッチ未適用の安定版リリース(多くのディストリビューションカーネルを含む)は脆弱です。tc peditフィルタを設定して、setuidバイナリのメモリ上のELFエントリポイントをシェルコードで上書きします。skb_ensure_writable() をキーループ内に移動しました)。回避策として、act_pedit モジュールをブロックまたはアンロードするか、権限のないユーザー名前空間を無効にします(例:sysctl user.max_user_namespaces=0)。緩和後は、キャッシュをドロップして(echo 3 > /proc/sys/vm/drop_caches)汚染されたページを追い出します。このレポートは、CVE-2026-46331の詳細な技術分析を提供します:その原因、悪用方法、検出、および修復戦略、ベンダー勧告、CVE、公開エクスプロイトへの参照を含みます。
定義: CVE-2026-46331は、LinuxカーネルのTraffic Control(net/sched)サブシステム、具体的には act_pedit(パケットエディタ)アクションにおける範囲外書き込みバグです。関数 tcf_pedit_act() は、型付きキーを反復処理する前に、パケット編集操作の「コピーオンライト」範囲を静的なヒント tcfp_off_max_hint を使用して計算します。ただし、一部のキー(例:TCP/UDPヘッダーの編集)は、最終的なバイトオフセットを実行時のみに決定します。コードはこれらの動的オフセットについて書き込み可能性を再チェックしません。その結果、事前にCOWされた領域の外側で書き込みが発生する可能性があります:パケット書き込みの一部がプライベートにされることはなく、部分的なCOW につながります。この誤った書き込みは、(パケットバッファがたまたまファイルページを参照している場合)ファイルの共有ページキャッシュメモリに伝播し、キャッシュされたファイルイメージを破壊します。
背景: Linuxの**パケットエディタ(pedit)**アクションは、管理者が設定された tc フィルタをパケットが通過する際に、パケットヘッダー(リンク層、ネットワーク層、またはトランスポート層)内の任意のバイトを書き換えることを可能にします。これは、オフセット(ヘッダーに固定される可能性がある)と32ビットの値/マスクを指定することで機能します。内部的には、pedit はソケットバッファ(sk_buff)を操作し、変更する前にターゲットのパケットメモリを書き込み可能にする必要があります(COW方式で skb_ensure_writable() を使用)。理想的には、カーネルは書き込む前に共有ページをクローン(プライベートコピー)して、他の場所で使用されているメモリを変更しないようにする必要があります。
根本原因: tcf_pedit_act() では、コードが誤って tcfp_off_max_hint(最大静的オフセット)を使用して書き込み可能範囲を最初に一度だけ計算します。このヒントには、パケットが処理されるときに型付きキーが追加する実行時ヘッダーオフセットが含まれません。TCPやUDPなどのキーは、実行時にIPヘッダーの位置に基づいてオフセットを計算できます(たとえば、以前のキーがネットワークヘッダーをシフトした場合)。したがって、キーごとのループ中に、キーの実際のオフセットが事前に書き込み可能として割り当てられた範囲を超える可能性があります。その後、コードは skb_store_bits() を介してパケットメモリに書き込みますが、事前にCOWされた領域を超えたページはプライベートにされていないため、その書き込みはページキャッシュとまだ共有されているページを破壊します。つまり、**「書き込み可能なパケット範囲を早期に計算しすぎる」**ことが、範囲外のページ間書き込みを引き起こします。負のオフセット(例:入力時のイーサネットヘッダーの編集)も誤って処理され、offset_valid() でさえ INT_MIN のガードがなく、この欠陥を悪化させています。
なぜ発生するのか: このバグは、基本的にはコピーオンライト範囲計算の論理エラーです。カーネルは、静的最大オフセット(ロード時に既知)がすべての編集に十分であると想定しました。動的オフセットを持つキーが実際に適用されたときに、COW範囲を更新できませんでした。一連のキューされた編集の後、最終的な書き込みは事前にチェックされた領域の外側にある可能性があります。パケットバッファはメモリマップされたファイルページを参照する可能性があるため(例:ゼロコピーメカニズムを介して)、この「部分的なCOW」書き込みはディスク上のファイルのページキャッシュに到達する可能性があります。実際には、パケットエディタアクションはsendfileまたはspliceからページを受け取る可能性があるため、単一のパケットフィルタ操作が間接的に攻撃者が選択したデータをファイルのインメモリイメージに書き込むことができ、ディスクは変更されません。
コンポーネントとデータフロー: 脆弱なコードはLinuxの net/sched サブシステム(act_pedit.c)にあります。パケットが設定された pedit ルールに一致すると、tcf_pedit_act() が呼び出されます。内部的には、skb_ensure_writable(skb, X) を正確に1回呼び出します。ここで X = tcfp_off_max_hint です。これにより、パケットの最初の X バイトがプライベート(COW処理)になります。次に、各キー(編集操作)をループし、キーの指定されたオフセットに実行時ヘッダーオフセットを加算してキーの実際の書き込みオフセットを計算し、32ビット値をパケットに書き込みます。疑似コードでは:```c
u32 off_max = action->tcfp_off_max_hint;
skb_ensure_writable(skb, off_max);
for (i = 0; i < num_keys; i++) {
u32 hdr_off = compute_header_offset(skb, key[i].hdr_type);
u32 write_off = hdr_off + key[i].offset;
skb_store_bits(skb, write_off, &key[i].value, 4);
}
Because `hdr_off` は各キーを処理するときにのみ計算されるため、最初の `skb_ensure_writable()` 呼び出しはそれを考慮していませんでした。`hdr_off + key[i].offset` が `off_max` を超えると、コードはメインの線形領域ではなくフラグメント上で `skb_store_bits()` にフォールバックし、プライベート化されていないページに書き込むことになります。これが失敗点です。
**攻撃対象領域:** 必要なインターフェースは、`pedit` アクションを備えた **tc フィルター** のみであり、通常は **CAP_NET_ADMIN** ケーパビリティが必要です。しかし、通常のユーザーは(ユーザー名前空間のクローン生成により)プライベートなネットワーク名前空間内で、実際の特権なしに CAP_NET_ADMIN を取得できます。したがって、非特権ユーザーはユーザー+ネット名前空間に入り、ループバック上に `tc pedit` ルールを作成できます。書き込みはパケットが処理されるときに発生します(攻撃者は通常、トリガーするためにループバック上でトラフィックを生成します)。カーネルは自身の COW 設定を信頼していましたが、ユーザー提供のオフセットがその前提を破ったため、信頼境界(ユーザー対カーネル)が越えられます。
**内部メカニズム:** カーネル側では、この脆弱性は **境界外書き込み**(CWE-787)として現れます。これはユーザー空間にマップされたカーネルメモリ(ファイルページキャッシュ)を破壊します。具体的には、ソケットバッファにたまたまマップされている任意のファイルページの内容を上書きできます。概念実証では、`/bin/su` をソケットバッファに送り込んで mmap するため、エクスプロイトはメモリ内のエントリポイントのバイトを反転させます。これはディスク上のファイルを変更しませんが、その後そのバイナリを実行すると、キャッシュから汚染されたイメージが読み込まれます。ブログの分析は次のように述べています:
> “skb は sendfile 経由で取り込まれたゼロコピーページを参照できるため、その境界外書き込みは実ファイルを支える共有ページキャッシュメモリに到達し得ます。カーネルはパケットメモリを変更しても安全な状態にしたと信じていますが、実際には、その後の書き込みは実際にプライベート化した領域の外にまで達します。”
**信頼境界:** カーネルは、`skb_ensure_writable()`(高速パス COW)がその後のすべての書き込みに対して安全性を保証すると誤って想定していました。各キーについて再チェックしなかったのです。ユーザーが制御できるのはパケットフィルタの設定とパケットコンテンツのみで、カーネルは(ネットワーク名前空間を通じて)それを許可していました。その信頼が破られると、書き込みは保護されるべきだったファイルバッキングメモリへと逃れてしまいました。
## 根本原因分析
根本原因は、**pedit アクションにおける COW 範囲の誤った計算**です。コード的には、単一の `skb_ensure_writable()` が `tcfp_off_max_hint` に基づく長さで呼び出され、その後ループ内で実際のオフセットがこれを超える可能性がありました。小さなパッチ(2026年5月)は、真のオフセットが判明した後に `skb_ensure_writable()` をループの *内側* に移動し、負のオフセットに対するチェックと特別な処理を追加することで修正しています。言い換えれば:
- **バグのあるコード:** ```c
skb_ensure_writable(skb, action->tcfp_off_max_hint);
for each key:
// compute offset (hdr_off + key_offset)
skb_store_bits(skb, write_off, ...);
さらに、この修正では、負のオフセット(イーサネットヘッダー編集)の場合にヘッドルームに対して skb_cow() を使用し、INT_MIN ケースを防ぎます。コミットメッセージ(stack.watch の要約)には次のようにあります:「実際の書き込みオフセットが判明している per-key ループ内に skb_ensure_writable() を移動し、オフセット演算にオーバーフローチェックを追加して修正する。」
つまり、なぜこのバグが存在するのか: コードレビューや設計の際に、キーごとの再計算が見落とされていました。静的ヒント最適化により、キーごとに再評価する必要性が回避されました。これは悪意のある見落としというよりも、単純なバグのように見えますが、COW の前提に違反するため、その影響は深刻です。TuxCare が指摘するように、このバグは日常的な「データ破損」修正を装ってマージされ、即時のセキュリティコンテキストがないままでした。
この脆弱性は、カーネルコミット 8b796475fd78(2022年5月)によって導入され、2026年初頭まで気付かれないままでした。情報源によると、修正(2026年5月31日のコミット 899ee91156e5)は、通常のデータ破損パッチとして netdev メーリングリストに提出されました。カーネルメンテナは、2026年6月4日にその修正(net-7.1-rc7)をマージしました。CVE-2026-46331 が正式に割り当てられたのは、2026年6月16日になってからでした(パッチの出現から約2週間後)。完全に武器化された公開エクスプロイトは、2026年6月17日に登場しました(packet_edit_meme PoC)。
実際には、その経緯は次のとおりです:
公開されたパッチによって、複数の関係者がこのバグに気付きました。例えば、サイバーセキュリティ研究者の Massimiliano Oldani は、その直後に詳細な解説とエクスプロイトを公開し、「CVE 割り当てから24時間以内に、packet_edit_meme という名前の実用的な概念実証エクスプロイトが GitHub に登場した」 と述べています。CloudLinux、TuxCare、SentinelOne は、PoC が公開され CVE が割り当てられた後、分析を公開しました。Debian セキュリティトラッカーと PT DBugs も、この問題と利用可能なアドバイザリをまとめています(参考文献を参照)。
現実的な攻撃には、最小限の前提条件しか必要ありません:
攻撃者の能力: 対象マシン上のローカルの非特権ユーザー。そのユーザーは、unshare(CLONE_NEWUSER|CLONE_NEWNET) を使用して、ネットワーク名前空間を伴う新しいユーザー名前空間を作成できなければなりません。これにより、実際の root 権限なしで、その名前空間内で CAP_NET_ADMIN が付与されます。非特権ユーザー名前空間は多くのカーネル(例:RHEL、Debian)でデフォルトで有効になっており、Ubuntu では aa-exec の回避策で再び有効にできます。
対象条件: 対象は、act_pedit モジュールが利用可能な脆弱な Linux カーネル(約 5.18〜7.1-rc6)を実行している必要があります。act_pedit が組み込まれているか、すでにロードされている場合、すぐに悪用可能です。モジュールの場合は、tc pedit ルールが構成されると自動的にロードされます。対象は、アップストリームパッチを適用していない必要があります。特に、攻撃者がファイルへの書き込みアクセス権を持つ必要はありません。このエクスプロイトは、パケットフィルタを介した書き込みによって機能します。