needhelp
← ブログに戻る

Dirty Frag:Linux カーネルゼロコピー権限昇格脆弱性の深層解析

著者 xingwangzhe
Linux
カーネル
セキュリティ
権限昇格
Dirty Frag
CVE

Dirty Frag:Linux カーネルのゼロコピーパス上でのページキャッシュ書き込み脆弱性チェーン。xfrm-ESP と RxRPC の2つの独立した脆弱性を連結し、2017年以降のほぼすべての主流ディストリビューションに影響を与え、パスワードなしで root 権限に昇格可能。


またかよ?静かにさせてくれないのか

正直なところ、こんなに早くまたカーネル権限昇格の記事を書くことになるとは思っていなかった。

前回の Copy Fail の記事からわずか 8日 だ。8日だぞ!Copy Fail の algif_aead ブラックリストをターミナルに打ち込んだばかりで、パッチもまだ温かいうちに、また降ってきた — Dirty Frag だ。

さらに馬鹿げているのは、Copy Fail の緩和策が Dirty Frag に対して 完全に無効 という点だ。今回はまったく異なるカーネルサブシステム — xfrm-ESP と RxRPC の暗号化パス — を攻撃しているので、algif_aead を無効にしようがしまいが関係ない。

Dirty Pipe(2022)、Copy Fail(2026.04)、Dirty Frag(2026.05)……毎回「2017年以降のほぼすべてのディストリビューションに影響」と言っている。これが何を意味するかわかるか?過去10年近く、システム内の任意の非特権アカウントにログインした者が、こっそり root になれるチャンスがあったということだ。

だがよく考えると、これは全部カーネル開発者の責任なのか?必ずしもそうではない。

ここ数年の AI 支援コード監査ツールの爆発的な成長により、セキュリティ研究者は 数時間で 10年近く潜んでいた脆弱性を発見できるようになった。以前は人力で一行一行レビューしなければ見つからなかったバグが、今では AI が一括スキャンして次々と発見される。Copy Fail は AI ツール Xint Code が1時間で特定した。Dirty Frag は韓国の研究者 Hyunwoo Kim による人工監査だが、影響を与えるコードパスは Copy Fail と同じファミリー — ゼロコピーパス上のページキャッシュ書き込み — に属しており、この種の設計欠陥が一握りの孤立したケースにとどまらないことを示している。ツールが強くなり、効率が上がり、自然と脆弱性が「増える」。

さらに不安なのは今回の開示方法だ。研究者は当初、カーネルセキュリティチームに脆弱性の詳細を提出し、ディストリビューションにパッチ適用の時間を与えるために 5日間のエンバーゴ を約定していた。ところが 当日、無関係な第三者が ESP(xfrm) 脆弱性の完全な詳細とエクスプロイトを 直接公開 してしまった。CVE もなく、パッチもないのに、PoC は誰でも実行できる状態になった。これは責任ある開示ではなく、すべての Linux ユーザーに背中からナイフを突き刺す行為だ — パッチ適用の窓が完全に破壊され、全員が裸のまま走らされることになった。

よし、文句はこれくらいにして。今回の脆弱性がどういうものか、真面目に見ていこう。


タイムライン

日付出来事
2017-01xfrm-ESP 脆弱性がコミット cac2661c53f3 でカーネルに導入(9年間 潜伏)
2023-06RxRPC 脆弱性がコミット 2dc334f1a63a で導入
2026-04-29韓国の研究者 Hyunwoo Kim (@v4bel) が RxRPC 脆弱性と完全なエクスプロイトを security@kernel.org に報告
2026-05-07脆弱性詳細が linux-distros メーリングリストに提出され、5日間のエンバーゴ を約定
2026-05-07同日、第三者が ESP(xfrm) 脆弱性の詳細情報とエクスプロイトを公開し、エンバーゴは即座に破棄
2026-05-07各ディストリビューションのメンテナーと協議の上、完全な Dirty Frag 文書が公開。この時点で CVE なし、公式パッチなし
2026-05-08本文執筆時点で、主要ディストリビューションはまだアップストリームのパッチ統合を待っている

影響性分析

指標詳細
CVE未割り当て(エンバーゴ破棄時に NVD が割り当てる暇がなかった)
脆弱性タイプ決定論的論理欠陥、競合状態ではない
悪用成功率100%、一度実行すれば必ず成功
影響範囲2017年以降のほぼすべての主流 Linux ディストリビューション(2026年5月時点で、最新カーネル 7.0.3 も影響を受ける)
悪用方法192バイトのペイロードを、48回の4バイト書き込みで root-shell ELF を組み立てる
ディスク痕跡永続的変更なし — メモリ内のページキャッシュのみを汚染し、inotify を回避;再起動で復元
Copy Fail 緩和策の回避Copy Fail の algif_aead 無効化は 完全に無効、異なるサブシステムを攻撃するため

過去のカーネル LPE 脆弱性との比較:

特性Dirty Cow (2016)Dirty Pipe (2022)Copy Fail (2026)Dirty Frag (2026)
競合状態必要不要不要不要
影響範囲特定バージョン5.8+2017年以降の全主流ディストリビューション2017年以降の全主流ディストリビューション
悪用コード量複雑複雑Python 10行単一ファイル C プログラム
ディスク痕跡有り有り無し無し
パッチ状況修正済み修正済み修正済み公式パッチなし

影響が確認されたディストリビューション:

ディストリビューションテストしたカーネルバージョン
Ubuntu 24.04.46.17.0-23-generic
RHEL 10.16.12.0-124.49.1
CentOS Stream 106.12.0-224
AlmaLinux 106.12.0-124.52.3
Fedora 446.19.14-300
openSUSE Tumbleweed7.0.2-1
Arch Linux7.0.3

6.12 から 7.0 まで、Ubuntu から Arch まで、全プラットフォームを通殺。そう、インストールしているほぼすべての主流ディストリビューションが影響を受けるだろう。


技術原理:なぜ Dirty Frag なのか?

一言で言えば

splice() が読み取り専用ファイルの ページキャッシュ参照 をネットワーク送信バッファ(skb)の frag スロットに埋め込む → 受信側カーネルが frag に対して インプレース暗号操作(in-place crypto、src == dst が同じメモリを指す)を実行する → 暗号文に対する復号操作が、読み取り専用ページキャッシュへの直接書き込み STORE プリミティブ に変わる → su の機械語を root-shell ELF に置き換える。

「Dirty」はページキャッシュの汚染を、「Frag」は skb(socket buffer)のフラグメント機構の悪用を指す。合わせて — Dirty Frag

ゼロコピーパス上の skb frag 汚染

これは脆弱性全体を理解する上で鍵となる部分なので、詳しく説明する。

通常、ソケットにデータを書き込むと、カーネルはユーザ空間のデータを コピー してカーネルの skb に入れる。しかし splice()ゼロコピー パスを通る — データそのものをコピーせず、ファイルページキャッシュページへのポインタ(page 構造体 + オフセット)を skb の frag 配列に直接詰め込む:

struct skb_shared_info {
    struct sk_buff  *frag_list;      // frag リンクリスト
    skb_frag_t      frags[MAX_SKB_FRAGS];  // frag 配列、各要素は {page, offset, size}
    // ...
};

ここで重要な点:splice() に渡される page は、ファイル(例えば /usr/bin/su)の メモリ内ページキャッシュページ だ — 読み取り権限しかないが、このページへの参照は既にネットワークプロトコルスタックの skb に詰め込まれている。

次は、受信側のネットワークプロトコルスタックがこのページに「余計なことをして」書き込むかどうかだ。

脆弱性その1:xfrm-ESP ページキャッシュ書き込み

最初の脆弱性は IPsec ESP(Encapsulating Security Payload)の復号パスにある。

esp_input() 関数は暗号文データを復号するために使われる。通常、skb のデータ領域が frag と共有される場合、カーネルは先に skb_cow_data()(Copy-on-Write)を呼んで共有ページをコピーしてから操作すべきだ。しかし、esp_input() には cow を迂回するコードパスが存在する:

static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
    if (!skb_cloned(skb)) {
        if (!skb_is_nonlinear(skb)) {
            // [1] 線形 skb:head データのみ、frag なし、安全
            nfrags = 1;
            goto skip_cow;
        } else if (!skb_has_frag_list(skb)) {
            // [2] frag があるが frag_list がない → cow を直接スキップ!
            nfrags = skb_shinfo(skb)->nr_frags;
            nfrags++;
            goto skip_cow;  // 爆弾はここ
        }
    }
    // 通常パス:データをコピー、安全
    err = skb_cow_data(skb, 0, &trailer);
}

skb が非線形(splice で渡されたページキャッシュを保存した frag がある)だが frag_list が空の場合、コードは直接 skip_cow にジャンプする。その後、crypto_authenc_esn_decrypt() がインプレースで AEAD 復号を行う — src と dst は 同じ scatterlist を指し、つまり攻撃者が埋め込んだページキャッシュページだ。

復号プロセス中に、特に重要なコード行がある:

// シーケンス番号の上位4バイトを dst SGL の末尾に移動
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);

このコード行は dst(つまり su のページキャッシュページ)の assoclen + cryptlen オフセットに 4バイト を書き込む。そして tmp + 1 の値は ESP ヘッダのシーケンス番号上位32ビットから来る — 攻撃者は XFRM SA(Security Association)を登録する際に、XFRMA_REPLAY_ESN_VAL.seq_hi を通じてこの値を完全に制御できる。

つまり、攻撃者は同時に以下を制御する:

  • どこに書くか(ファイルオフセット、ペイロード長を調整して位置決め)
  • 何の値を書くか(4バイト、seq_hi で指定)

48個の異なる XFRM SA を登録し、各 SA の seq_hi に ELF の4バイト断片を格納し、48回ループすれば、完全な root-shell ELF が su のページキャッシュに組み立てられる。

ただしこの脆弱性には制限がある:XFRM SA の登録には CAP_NET_ADMIN 権限が必要だ。攻撃者はユーザー名前空間を作成することで(unshare(CLONE_NEWUSER | CLONE_NEWNET))この権限を得られるが、Ubuntu の AppArmor は非特権ユーザーがネットワーク名前空間を作成するのを阻止している。

だから2つ目の脆弱性が必要なのだ。

脆弱性その2:RxRPC ページキャッシュ書き込み

2つ目の脆弱性は RxRPC プロトコルの Kerberos 認証復号パスにある。

rxkad_verify_packet_1() 関数は、受信したデータパケットの先頭8バイトに対してインプレース pcbc(fcrypt) 復号を実行する:

skcipher_request_set_crypt(req, sg, sg, 8, iv.x);
//                               ^^  ^^
//                             src==dst → インプレース操作!
ret = crypto_skcipher_decrypt(req);  // 8バイト書き込みがここで発生

skb_to_sgvec() は skb の frag(攻撃者が splice で入れたページキャッシュページを含む)を直接 scatterlist に変換し、src と dst は 同じ sg だ。したがって復号操作は、8バイトの「復号結果」をその読み取り専用ページキャッシュページに直接書き戻す。

xfrm-ESP と比較すると:

特性xfrm-ESPRxRPC
書き込みサイズ4バイト8バイト
値の制御直接制御(seq_hi間接的(fcrypt 鍵のブルートフォースが必要)
必要な特権ユーザー名前空間特権不要
導入時期2017-012023-06

RxRPC 経由で書き込まれる値は直接制御できない — それは fcrypt_decrypt(C, K) の結果だ。攻撃者は先に add_key("rxrpc", ...) で鍵 K を登録し、ユーザ空間でブルートフォースして、目標の8バイト平文を生成する K を見つける必要がある。幸い、fcrypt は56ビット鍵、8バイトブロックの Andrew File System 専用暗号なので、ブルートフォースは大した問題ではない。

最も重要な点:RxRPC パスは 完全に特権を必要としない。ユーザー名前空間の作成も、ネットワーク名前空間も、CAP_NET_ADMIN も不要だ。しかも Ubuntu はデフォルトで rxrpc.ko モジュールをロードする。

連結ロジック

2つの脆弱性は互いを補完し、完全なカバレッジを形成する:

シナリオ使用する脆弱性理由
Ubuntu(AppArmor が名前空間を阻止)RxRPCrxrpc.ko がデフォルトでロードされ、特権不要
RHEL / Fedora / openSUSExfrm-ESP名前空間が利用可能、ESP 書き込みが精密に制御可能
その他のディストリビューションxfrm-ESP または RxRPCモジュールのロード状況に応じて選択、少なくとも1つのパスが利用可能

これが Dirty Frag の恐ろしいところだ — どう設定しようが、root への道は必ずある


PoC 分析

完全なエクスプロイトは github.com/V4bel/dirtyfrag でオープンソース化されている。コンパイルして実行するのに必要なのは1行だけだ:

git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag && gcc -O0 -Wall -o exp exp.c -lutil && ./exp

核心のアイデアはそれほど複雑ではない:

  1. 192バイトの最小限 ELF を準備する — 実行すると権限昇格する root-shell
  2. この192バイトを48個の4バイトチャンクに分割する(xfrm-ESP パスを使う場合)
  3. 各4バイトチャンクに対して1つの XFRM SA を登録し、その seq_hi をそのチャンクの値に設定する
  4. 毎回 splice()su のページキャッシュを skb に送り、インプレース書き込みをトリガーする
  5. 48回ループし、su のページキャッシュ領域(先頭192バイト)を完全に root-shell ELF に置き換える
  6. execve("/usr/bin/su") → root shell

一切ディスクファイルには触れない。ディスク上の /usr/bin/su は微動だにせず、md5 は元のままだ。しかしカーネルのページキャッシュ内では、すでにすり替えられている。

任意のプロセスが su に対して execve を発行すると、カーネルはページキャッシュから読み取る — おっと、あなたが仕込んだバイナリが実行される。root 獲得。


応急処置:脆弱性モジュールの一時的無効化

現時点(2026年5月8日)では、公式カーネルパッチはまだリリースされていない。パッチが出るまでの一時的な緩和策は、3つの脆弱性モジュールをアンロードしてブラックリストに入れることだけだ。

まず、自分のシステムが影響を受けているか確認する:

lsmod | grep -E 'esp4|esp6|rxrpc'

何か出力があれば、影響を受けている。

直ちに以下のコマンドを実行する(再起動不要):

sudo sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf"
sudo rmmod esp4 esp6 rxrpc 2>/dev/null

確認:

lsmod | grep -E 'esp4|esp6|rxrpc'

重要:PoC が既に実行された可能性がある場合は、ページキャッシュをクリアしなければならない:

echo 3 | sudo tee /proc/sys/vm/drop_caches

これを実行しないと、モジュールを無効にしても、汚染されたページキャッシュがメモリに残ったままだ — su を実行すれば root shell になってしまう。

副作用:

  • esp4/esp6 を無効化すると IPsec VPN トンネルが中断される。デスクトップユーザーとほとんどのサーバーには影響しないが、IPsec VPN(strongSwan、Libreswan など)に依存している場合は、先に影響を評価すること。
  • rxrpc を無効化する影響は極めて小さい。AFS(Andrew File System)を使用していない限り問題ない。

AI 加速下の脆弱性軍拡競争

Dirty Frag の暴露は、もっと深い問題を考えさせられる:なぜ最近、カーネル LPE 脆弱性が次々と出てくるのか?

脆弱性発見時期主要ツール報告から公開まで
Dirty Pipe2022人工監査標準プロセス
Copy Fail2026-04Xint Code (AI)約1ヶ月
Dirty Frag2026-05人工監査8日(エンバーゴ当日破棄)

カーネル LPE 脆弱性は1年に1個から1ヶ月に1個になり、そして1ヶ月に1個から……1週間に1個になろうとしているのか?

その背景には、AI 支援コード監査ツールの爆発的な成長がある。以前は人力で一行一行レビューし、10年間誰にも発見されない脆弱性が普通だった。今や AI は数時間でサブシステム全体をスキャンし、すべての潜在的なゼロコピーパス、インプレース操作、共有参照を引き出す。ツールが強くなり、効率が上がり、自然と脆弱性が「増える」 — カーネルが突然悪くなったわけではなく、隅に隠された旧債が AI によって一つずつ暴かれているのだ。

さらに警戒すべきは開示エコシステムの変化だ。Dirty Frag のエンバーゴは第三者によって意図的に破棄され、脆弱性詳細と PoC が 同日 に公開された。これが何を意味するか?研究者が security@kernel.org に脆弱性を報告してから、世界中のハッカーが武器を手に入れるまで、8日 しかなかったということだ。パッチ適用の窓 — 消えた。

エンバーゴを破棄した人を非難できる。しかし現実には、こういうことは今後ますます増えるだけだ。AI は脆弱性発見を速くし、武器化も速くする。永遠にすべての人がエンバーゴの約束を守ることを期待することはできない。

そして忘れてはならない、同じ脆弱性ファミリーがまだ掘り出され続けている

脆弱性サブシステム状態
Copy FailAF_ALG + authencesn修正済み
Dirty Fragxfrm-ESP + RxRPCパッチなし
Copy Fail 2ESP-in-UDP公開済み
ZCRX Freelistio_uring ZCRX公開済み

この4つの脆弱性の核心原理は驚くほど似ている — splice() が読み取り専用ページキャッシュ参照をカーネルサブシステムに詰め込み、サブシステムが frag 上でインプレース書き込みを行う。これらが暴露しているのは、実は 同じクラスの設計問題 だ:

コンポーネント導入理由副作用
splice()ゼロコピー、性能最適化読み取り専用ページキャッシュ参照がカーネルサブシステムに送られる
AF_ALGカーネル暗号能力の公開非特権ユーザーが直接暗号セッションを開始できる
xfrm-ESPIPsec 加速インプレース復号、読み取り専用ページを出力バッファとして使う
RxRPCAFS ネットワークプロトコルサポート同上、名前空間特権すら不要

各設計を個別に見れば、どれも合理的な性能最適化または機能要件だ。しかし組み合わせると、任意のローカルユーザーがパスワードなしで root になれる 脆弱性チェーンになる。

アップストリームカーネルが「ゼロコピーパス上のインプレース操作」というパラダイムそのものを徹底的に見直さない限り、保証する — これが最後ではない

一般ユーザーへの私のアドバイスは単純だ:

  1. 今すぐ上記のブラックリストコマンドを実行する。 公式パッチが来たら元に戻せばいい。
  2. パッケージマネージャーのカーネル更新を密切に監視する。 修正版が出たら、直ちにアップグレードして再起動する。
  3. 定期的に lsmod | grep で、これらのモジュールが誤ってロードされていないか確認する。

参考リンク


インフォグラフィック

Dirty Frag 概要図

図1:Dirty Frag 脆弱性の概要 — ゼロコピーページキャッシュ参照が skb frags を通じてどのように悪用されるか

このページをシェア