
Dirty Fragは、特権のないユーザーがrootアクセスを取得できるようにするLinuxローカル特権昇格(LPE)脆弱性チェーンに関するレポートです。
Linuxカーネルのローカル権限昇格チェーンに対するエクスプロイト再現・検知ラボ。
ステータス: 検証済み。 ラボ(カーネル 6.18.9+kali-amd64)において、再現、ファイルレス検証、およびシステムコールレベルの検知を完了しました。本ドキュメントはラボログです。以下のすべての記述は再現実行中に観測されたものです。スクリーンショットと成果物はVMからの実際のキャプチャです。
Dirty Frag は、Linuxカーネルにおける2つの決定的なロジックバグを組み合わせたものです。これらのバグにより、権限のないローカルユーザーが読み取り専用ファイル(例: /usr/bin/su)のページキャッシュを上書きし、rootシェルを取得できます:
両バリアントは、Dirty Pipe および Copy Fail と同じ根本パターンを使用します。splice(2) システムコールは、ファイルのページキャッシュページへの参照を送信側 sk_buff の frag スロットに配置します。攻撃者はこのファイルを読み取り専用でアクセスできます。受信側のカーネルコードは、その frag 上でインプレース暗号STOREを実行します。これによりRAM内のページキャッシュが変更されます。ディスクへの書き込みは発生しないため、ファイル整合性モニタリング(AIDE、Tripwire)では検知できません。この攻撃は決定的です。レースウィンドウはなく、失敗時のカーネルパニックもありません。
cac2661c53f3(2017‑01)から f4c50a4034e6(2026‑05‑05 パッチ適用)まで2dc334f1a63a(2023‑06)から aa54b1d27fe0(2026‑05‑10 パッチ適用)までCVE-2026-43284 = 8.8(高)、CVE-2026-43500 = 7.8(高)Dirty Frag はファイルレスLPEです。ディスク上のファイルではなく、メモリ内のページキャッシュを破壊します。従来のファイル整合性モニタリングでは検知できません。検知はシステムコール層で行う必要があります。このチェーンは以下のシステムコールプリミティブを使用します: socket(AF_ALG)/socket(AF_RXRPC)、splice、および unshare(CLONE_NEWUSER|CLONE_NEWNET)。ESPパスは AF_INET UDP および netlink ソケットも作成します。この層が本リポジトリの検知エンジニアリングの焦点です。
両バリアントは同じシンクを使用します: 攻撃者が splice(2) で配置したページキャッシュページ上にバイトをSTOREするインプレース暗号です。
UDP_ENCAP_ESPINUDP で構成します。vmsplice でパイプに登録し、その後 splice で対象ファイルオフセットの /usr/bin/su から16バイトを登録します。splice がパイプを送信ソケットにプッシュします。splice_to_socket() は MSG_SPLICE_PAGES を設定します。これにより /usr/bin/su のページキャッシュページが skb->frags[0] に直接配置されます。xfrm4_udp_encap_rcv、続いて xfrm_input、続いて esp_input()。脆弱な skip_cow 分岐(!skb_cloned() && !skb_has_frag_list())は をバイパスします。ページキャッシュページをソースとデスティネーションの両方としてを実行します。攻撃者は場所(spliceオフセット)と値(4バイト)の両方を制御します。認証検証はストア後に実行されるため、暗号層が書き込みをフラグ付けすることはありません。このバリアントは CAP_NET_ADMIN を必要とし、unshare(CLONE_NEWUSER|CLONE_NEWNET) を使用します。
rxkad_verify_packet_1() は、spliceで固定されたskb frag上で直接、単一ブロックの pcbc(fcrypt) 復号を実行します。最初にデータをコピーしません。攻撃者はセッションキー(add_key("rxrpc", …))を選択し、decrypt(ciphertext) が desired_plaintext と等しくなるようにします。これにより8バイトのSTOREが生成されます。このバリアントは /etc/passwd をターゲットにします。ユーザー名前空間は不要です。rxrpc.ko モジュール(Ubuntuではデフォルトでロード)が必要です。
公開PoCは /usr/bin/su をターゲットにします。4バイトずつ48回のESPストア(ファイルオフセット0で192バイト)を書き込みます。最初のページキャッシュバイトを静的rootシェルELFに置き換えます。ELFエントリポイントは setgid(0); setuid(0); setgroups(0,NULL); execve("/bin/sh", …) を実行します。単一の execve("/usr/bin/su") でrootシェルが得られます。
ESPパッチ(メインライン f4c50a4034e6)は、splice() 経由で到着するページfragに SKBFL_SHARED_FRAG フラグをマークします。esp_input() の skip_cow 分岐は現在このフラグもチェックします。共有fragのskbは、インプレースAEAD復号の前に skb_cow_data() を経由します。
RxRPCパッチ(メインライン aa54b1d27fe0)は、既存の skb_cloned() チェックの隣に skb->data_len チェックを追加します。カーネルは、インプレース pcbc(fcrypt) 復号の前に、ページデータを含む非線形skbをコピーします。
VirtualBoxラボセットアップのスクリーンショット:

.
├── README.md # 本ラボログ
├── detection/
│ ├── dirtyfrag.rules # auditd システムコールレベル検知ルール
│ ├── ausearch_dirtyfrag_observed.txt # 実際のエクスプロイト検知出力
│ ├── sigma/
│ │ └── dirty_frag_exploit.yml # SIEM検知用Sigmaルール
│ └── yara/
│ └── dirty_frag_exploit.yar # ディスク/メモリ上のPoCコード用YARAルール
├── mitigation/
│ └── dirtyfrag_mitigation.sh # モジュールブラックリスト + ページキャッシュフラッシュ
├── poc/
│ └── check_vulnerable.py # 非破壊的な事前チェッカー
├── reports/
│ └── incident-dirtyfrag.md # インシデントレスポンスプレイブック
└── screenshots/ # ラボVMからの実際のキャプチャ
poc/check_vulnerable.py を実行し、カーネル/モジュール/usernsを確認testuser を作成/usr/bin/su ハッシュをキャプチャdrop_caches または再起動)detection/dirtyfrag.rules をデプロイし、auditdアラートを検証cat /etc/os-release | head -3
uname -r
カーネルバージョン確認のスクリーンショット:

カーネルは2026年5月の修正(f4c50a4034e6 / aa54b1d27fe0)より古い必要があります。その日付以降に apt upgrade を実行した場合、エクスプロイトは失敗します。トラブルシューティングを参照してください。
安全なチェッカーが、VMが対象となり得るかどうかを報告します。実行中のカーネル、esp4/esp6/rxrpc モジュールの存在、非特権ユーザー名前空間が利用可能かどうかをチェックします。ESPバリアントはこれらの名前空間を必要とします。
python3 poc/check_vulnerable.py
期待される判定: [*] potentially vulnerable -- proceed in a disposable VM only
非特権の testuser は、特別な権限のない攻撃者をシミュレートします。
sudo useradd -m testuser
sudo passwd testuser
su - testuser
id
スクリーンショット: id はUID 1001を示します。これは非rootアクセスを確認します。

非特権アカウントから:
git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag
gcc -O0 -Wall -o exp exp.c -lutil
./exp
成功すると、エクスプロイトは /usr/bin/su のページキャッシュをパッチします。4バイトずつ48回のESPストア(ファイルオフセット0で192バイトのrootシェルELF)を書き込みます。その後、forkpty でインタラクティブなrootシェルをドロップします。
モジュールの可用性、ソースレビュー、クリーンなコンパイルのスクリーンショット:

id
whoami
スクリーンショット: ./exp 実行後、id は uid=0(root) を示します。

sha256sum はページキャッシュ経由で読み取ります。エクスプロイトの書き込みがアクティブな間、ハッシュは元のものと異なります。フラッシュ後、ハッシュは元のものに戻ります。この前後のペアは、ディスク上のバイナリが決して変更されていないことを証明します。
sha256sum /usr/bin/su # 1) ページキャッシュが汚染されている間 -> 異なるハッシュ
スクリーンショット: ハッシュはパッケージハッシュと異なります(RAMが汚染され、ディスクは無傷)。

観測された破損(ページキャッシュ)ハッシュ: 3fc29078bd77150b5d6fbb368f632bf02c1a3704816f03fb694ec2e81e77bde4
エクスプロイト後、ページキャッシュには破損したデータが含まれます。常にフラッシュしてください:
echo 3 | sudo tee /proc/sys/vm/drop_caches
# またはVMを再起動
その後、復元を確認します(testuser として):
sha256sum /usr/bin/su # 元のパッケージハッシュと一致する
su - # 再度パスワードを要求される — 自動rootなし
スクリーンショット: 復元された(ディスク上の元の)ハッシュ。

観測された元の(ディスク上の)ハッシュ: 2b4f8770bd35bba5cdc5cfe292bc1d988e92ec1786bf91cf83e0e86fac056eb6
再起動後、dpkg -V util-linux は出力なしを返しました。ディスク上の /usr/bin/su はパッケージと完全に一致するため、破損した 3fc29078… ハッシュはページキャッシュにのみ存在していました。
drop_cachesは汚染されたページを追い出せない場合があります。 これは、実行中のプロセスがまだページをピン留めしている場合に発生します。その場合、sha256sumとdpkg -Vは破損したコンテンツを表示し続けます。信頼できる修正は再起動です。dpkg -Vもページキャッシュ経由で読み取ることに注意してください。ページが汚染されている間、??5??????を報告します(MD5が異なります。サイズ、モード、所有者、mtimeはすべて一致します)。再起動後は再び沈黙します。これはディスク上のファイルが決して変更されていないことを証明します。
フラッシュはエクスプロイトを無効化しません。汚染されたページキャッシュをクリアするだけです。モジュールは別途無効化する必要があります(緩和策を参照)。すでにロードされているモジュールを悪用されたホストから削除するには再起動が必要です。
Dirty Frag はファイル整合性モニタリングでは見えません。検知は、チェーンが必ず使用するシステムコールプリミティブに焦点を当てます。
detection/dirtyfrag.rules)# /etc/audit/rules.d/dirtyfrag.rules
-a always,exit -F arch=b64 -S socket -F a0=38 -F uid!=0 -k dirtyfrag_af_alg
-a always,exit -F arch=b64 -S socket -F a0=33 -F uid!=0 -k dirtyfrag_rxrpc
-a always,exit -F arch=b64 -S splice -F uid!=0 -k dirtyfrag_splice
-a always,exit -F arch=b64 -S unshare -F uid!=0 -k dirtyfrag_namespace
-w /usr/bin/su -p r -k dirtyfrag_suid_read
注: Linuxでは
AF_ALG = 38、AF_RXRPC = 33です(/usr/include/bits/socket.hを参照)。以前のドラフトではa0=21を使用していました。その値は誤りです。AF_RXRPC は 33 であり、21ではありません。
デプロイと検証:
sudo cp detection/dirtyfrag.rules /etc/audit/rules.d/
sudo systemctl restart auditd # または: sudo auditctl -D && sudo auditctl -R /etc/audit/rules.d/dirtyfrag.rules
sudo auditctl -l
エクスプロイトを再実行した後の期待されるアラート(PIDで相関):
ausearch -k dirtyfrag_af_alg
ausearch -k dirtyfrag_rxrpc
ausearch -k dirtyfrag_splice
ausearch -k dirtyfrag_namespace
5つのルールすべてがロードされました(auditctl -R、各 CONFIG_CHANGE で res=1)。ルールはエクスプロイトの名前空間セットアップを捕捉しました:
time->Wed Aug 5 08:26:37 2026
type=PROCTITLE msg=audit(1785932797.328:592): proctitle="./exp"
type=SYSCALL msg=audit(1785932797.328:592): arch=c000003e syscall=272 success=yes exit=0 a0=50000000 a1=0 a2=0 a3=0 items=0 ppid=5198 pid=5199 auid=1000 uid=1001 gid=1001 euid=1001 suid=1001 fsuid=1001 egid=1001 sgid=1001 fsgid=1001 tty=pts2 ses=2 comm="exp" exe="/home/testuser/dirtyfrag/exp" subj=unconfined key="dirtyfrag_namespace"
デコード: syscall=272(unshare)、a0=50000000 は CLONE_NEWUSER | CLONE_NEWNET に等しい、uid=1001(非特権 testuser)、comm="exp"。完全なログは detection/ausearch_dirtyfrag_observed.txt にあります。
スクリーンショット: ausearch出力はルールのロードとエクスプロイトイベントを示します。

完全なプレイブックは reports/incident-dirtyfrag.md にあります: エグゼクティブサマリー、タイムライン、IoCs、MITRE ATT&CKマッピング、封じ込め、根絶、復旧、および教訓。
即時のランタイム緩和策。すでにロードされているモジュールについては再起動後も持続しません(下記の注を参照):
sudo mitigation/dirtyfrag_mitigation.sh
実行内容:
/etc/modprobe.d/dirtyfrag.conf に esp4、esp6、rxrpc をブロックする設定を書き込みます。blacklist 行と alias … off 行が含まれます。単純なワンライナーではこれらの行が欠落します(それ以外の場合、エイリアスによる自動ロードが機能していました)。影響: これらのモジュールを無効化すると、IPsec VPN(ESP)とAFSファイルシステム(RxRPC)の機能が停止します。
恒久的な修正: 上流パッチを含むカーネルにアップグレードします。または、起動時にモジュールをブラックリスト化します(initcall_blacklist=esp4,esp6,rxrpc)。
本リポジトリは許可された防御的セキュリティ研究およびトレーニングのみを目的としています。
LICENSE を参照してください。本ラボは、自身の隔離された環境での教育用です。テストを許可されていないシステムでPoCを実行しないでください。
| バリアント | CVE | シンク | トリガーパス | 非特権ユーザー名前空間が必要か |
|---|
| xfrm‑ESP ページキャッシュ書き込み | CVE‑2026‑43284 | esp_input() 内の crypto_authenc_esn_decrypt() | UDP‑encap を使用した socket(AF_INET)、続いて xfrm_input() | はい(CAP_NET_ADMIN) |
| RxRPC ページキャッシュ書き込み | CVE‑2026‑43500 | rxkad_verify_packet_1()(pcbc(fcrypt)) | socket(AF_RXRPC) | いいえ |
skb_cow_data()crypto_authenc_esn_decrypt() はESNの上位32ビットのSTOREを発行します。その値は replay_esn->seq_hi です。攻撃者はSA登録時に XFRMA_REPLAY_ESN_VAL netlink属性でこの値を選択します。| コンポーネント | 詳細 |
|---|
| ハイパーバイザー | VirtualBox |
| ターゲットVM | Kali Linux 2026.1(脆弱な状態にスナップショット復元) |
| カーネル | 6.18.9+kali‑amd64(2026年5月の修正より古い) |
| エクスプロイトPoC | V4bel/dirtyfrag(単一Cファイル) |
| 検知 | auditd(detection/dirtyfrag.rules のルール) |
reports/incident-dirtyfrag.md| レイヤー | 検知内容 | ステータス |
|---|
| auditd | AF_ALG および AF_RXRPC ソケット、splice、unshare、SUID読み取り | はい。デプロイ済み・検証済み(ルールファイル detection/dirtyfrag.rules) |
| Sigma | SIEM用システムコールパターン | はい。ルール準備済み(detection/sigma/dirty_frag_exploit.yml) |
| YARA | ディスクまたはメモリ上のPoCコード | はい。ルール準備済み(detection/yara/dirty_frag_exploit.yar) |
| FIM(AIDE/Tripwire) | ファイル変更 | いいえ。ディスク書き込みが発生しないため検知不可 |
| 症状 | 考えられる原因 | 修正 |
|---|
./exp が failed / post-write verify failed を出力 | カーネルがパッチ適用済み(2026年5月以降) | 古いカーネルで起動するか、更新前のスナップショットを復元。poc/check_vulnerable.py で再確認 |
unshare(CLONE_NEWUSER) が EPERM を返す | 非特権ユーザー名前空間が無効(AppArmorまたはsysctl) | sysctl kernel.unprivileged_userns_clone を確認。UbuntuではAppArmorを確認。Kaliはデフォルトで許可 |
esp4/esp6/rxrpc がロードされていない | モジュールが利用不可 | sudo modprobe esp4 esp6 rxrpc(RxRPCは socket(AF_RXRPC) で自動ロード) |
su バイナリが「パッチ済み」だがrootシェルなし | drop_caches がすでに実行された、またはページキャッシュがプロセスにピン留めされている | エクスプロイトを再実行。スタックした場合はVMを再起動 |
| エクスプロイト実行後、緩和策後もroot化可能 | ページキャッシュがフラッシュされていない、またはブラックリスト前にモジュールがすでにロードされている | echo 3 > /proc/sys/vm/drop_caches。完全に停止するには再起動が必要 |