
コピー失敗:主要なLinuxディストリビューションすべてでルートに732バイト。
732バイト。あらゆるディストリビューション。Root。
Linuxカーネルの
authencesn暗号テンプレートにおける直線的なロジック欠陥により、権限のないローカルユーザーが、setuidバイナリを含む任意の読み取り可能なファイルのページキャッシュに対して、正確で制御された4バイト書き込みを実行できます。レースなし。リトライなし。再コンパイル不要。2017年以降に出荷された主要なLinuxディストリビューションすべてでrootを取得できます。
📄 技術解説 · 🔗 カーネルパッチ · 🛡️ CVSS: Critical
| ディストリビューション | カーネルバージョン |
|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 |
| SUSE 16 | 6.12.0-160000.9-default |
これら4つすべてが、変更なしの同一の732バイトPythonスクリプトでroot化されました。
AF_ALGはカーネルの暗号サブシステムを権限のないユーザースペースに公開します。splice()はファイルデータを参照渡しでパイプに転送します — ページキャッシュページをコピーせずに直接渡します。ユーザーがファイルをAF_ALG AEADソケットにスプライスすると、ソケットの入力scatterlistはそのファイルのカーネルキャッシュページへのライブ参照を保持します。
algif_aead.cでは、2017年のインプレース最適化により、TX scatterlistからAADと暗号文をRXバッファにコピーしましたが、認証タグページはsg_chain()を使用して参照でチェーンし、その後req->src = req->dstを設定しました:
入力 SGL: [ AAD | CT | Tag ]
^
└─ sg_chain() → 依然としてページキャッシュページを指す
出力 SGL: [ AAD | CT ] ──→ [ Tag (ページキャッシュページ) ]
(RXバッファ) (TX SGLからチェーン)
req->src ──┐
├──→ 同じ結合scatterlist
req->dst ──┘
splice()からのページキャッシュページは、書き込み可能な宛先scatterlist内に配置され、正当な書き込み領域からはオフセット境界だけで分離されていました。アルゴリズムが境界内に留まることを強制するAPIは何もありませんでした。
authencesnの範囲外スクラッチ書き込みauthencesnは、IPsecで64ビット拡張シーケンス番号(ESN)サポートのために使用されるAEADラッパーです。HMAC計算用にESNバイトを再配置するために、呼び出し元の宛先バッファをスクラッチ領域として使用します — これには、認証タグ境界を超えたオフセットassoclen + cryptlenでの書き込みが含まれます:
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); // AAD[0..7]を読み取り
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); // dst[4..7]を上書き
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); // ← タグを超えて書き込み
3番目の呼び出しは、dst[assoclen + cryptlen]に4バイト(seqno_lo)を書き込みます。インプレースAF_ALGパスでは、scatterwalkはRXバッファからチェーンされたページキャッシュタグページへと交差します。カーネルはkmap_local_pageを介してページキャッシュページをマップし、ターゲットファイルのキャッシュされたコピーに直接書き込みます。
その後HMACは失敗し(暗号文は偽造されています)、recvmsg()はエラーを返します — しかし4バイトの書き込みは永続的に残ります。
個々の変更はどれも単独では間違っていませんでした。脆弱性は3つすべての交差点に存在します。
デフォルトのターゲットは/usr/bin/suで、テスト済みの全ディストリビューションに存在するsetuid-rootバイナリです。
ステップ1 — ソケット設定
AF_ALGソケットを開き、authencesn(hmac(sha256),cbc(aes))にバインド
キーを設定。リクエストソケットを受け入れ。(権限は不要。)
ステップ2 — 書き込みループ(4バイトのシェルコードチャンクごとに1回)
sendmsg() → AADバイト[4:8]が書き込む4バイトを運ぶ(seqno_lo)
splice() → ターゲットファイルのページキャッシュページをAF_ALGソケットに
recv() → 復号をトリガー → authencesnがseqno_loをページキャッシュに書き込む
(recvmsgはエラーを返しますが、書き込みは永続します)
ステップ3 — 実行
execve("/usr/bin/su")
カーネルは(現在破損している)ページキャッシュからバイナリをロード
Setuid-rootバイナリが注入されたシェルコードを実行 → UID 0
a = socket.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
# ... キーを設定し、リクエストソケットuを受け入れる ...
u.sendmsg([b"A"*4 + payload_chunk], [cmsg_headers], MSG_MORE)
os.splice(target_fd, pipe_wr, offset)
os.splice(pipe_rd, alg_fd, offset)
u.recv(...) # ページキャッシュ書き込みをトリガー
patch a664bf3d603dを含むカーネルに更新してください。この修正はalgif_aead.cをアウトオブプレース動作に戻します: req->srcはTX SGLを指し、req->dstはRXバッファを指します。splice()からのページキャッシュページは読み取り専用のままです。それらを書き込み可能な宛先にリンクしていたsg_chain()メカニズムは削除されます。
// 修正前(脆弱): srcとdstが同じscatterlistを共有
aead_request_set_crypt(&areq->cra_u.aead_req, rsgl_src, rsgl_src, used, ctx->iv);
// 修正後(修正済み): srcはTX SGL、dstはRXバッファ — 完全に分離
aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, rsgl_dst, used, ctx->iv);
algif_aeadカーネルモジュールを無効化:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
または、ワークロードプロファイルのseccompポリシーでAF_ALGソケット作成をブロックします。
コンテナ環境に関する注意: ページキャッシュはホスト全体で共有されるため、この脆弱性はコンテナ境界を越えます。ポッド単位ではなくノードレベルで緩和策を適用してください。完全なKubernetesエスケープの詳細はパート2を参照してください。
Theoriの研究者Taeyang Leeは、以前のkernelCTFでの作業を通じて、AF_ALG + splice()が権限のないユーザースペースがページキャッシュページを暗号サブシステムに直接供給できる経路を作り出すこと、そしてscatterlistページの出所が十分に調査されていない脆弱性クラスであることを特定しました。
研究チームはXint Codeを使用して、この洞察を以下のオペレータープロンプトでcrypto/サブシステム全体に拡張しました:
"これはLinux crypto/サブシステムです。ユーザースペースのsyscallから到達可能なすべてのコードパスを調べてください。1つの重要な観察に注意してください: splice()は読み取り専用ファイル(setuidバイナリを含む)のページキャッシュ参照をcrypto TX scatterlistに配信できます。"
約1時間の自動分析の後、Copy Failが最も深刻度の高い出力でした。同じスキャン中に発見された追加の脆弱性は、引き続き協調開示中です。
パート2: Podからホストへ — Copy Failが主要なクラウドKubernetesプラットフォームすべてからエスケープする方法。近日公開予定。
| 特性 | 詳細 |
|---|
| 決定的 | 直線的なロジック欠陥 — レースコンディションなし、タイミングウィンドウなし、リトライなし |
| 移植性 | 同じスクリプト、同じバイト列で、テスト済みの全ディストリビューションとアーキテクチャで動作 |
| 超小型 | 標準ライブラリのみ(os、socket、zlib)を使用する732バイトのPythonスクリプト。os.spliceにはPython 3.10+が必要 |
| ステルス性 | 破損したページはダーティとしてマークされません。ディスク上のチェックサムは変更されず、メモリ内のページキャッシュのみが変更されます |
| コンテナ横断 | ページキャッシュはコンテナ境界を越えてシステム全体で共有されます — これはKubernetesノードエスケーププリミティブでもあります(パート2参照) |
| 変数 | 制御方法 |
|---|
| ターゲットファイル | 現在のユーザーが読み取り可能な任意のファイル |
| 書き込みオフセット | assoclen、spliceオフセット、splice長 |
| 書き込み値 | sendmsg()で提供されるAADのバイト4〜7(seqno_lo) |
| 年 | 出来事 |
|---|
| 2011 | IPsec ESNサポート用にauthencesnがカーネルに追加されました(a5079d084f8b)。スクラッチ書き込みは存在しましたが無害でした — 内部のxfrmレイヤーのみが呼び出し、AADは別のscatterlistにありました。 |
| 2015 | AF_ALGがAEADサポートを獲得。authencesnが新しいAEADインターフェースに変換され(104880a6b470)、assoclen + cryptlen書き込みオフセットが導入されました。依然としてアウトオブプレース: ページキャッシュページはsrc(読み取り専用)にありました。まだ悪用可能ではありませんでした。 |
| 2017 | algif_aead.cにインプレース最適化が追加されました(72548b093ee3)。req->src = req->dst。ページキャッシュタグページが書き込み可能な宛先にチェーンされました。脆弱性が形成されました。 |
| 2026-03-23 | Linuxカーネルセキュリティチームに報告。 |
| 2026-04-01 | パッチがmainlineにマージ。 |
| 2026-04-22 | CVE-2026-31431が割り当てられました。 |
| 2026-04-29 | 公開開示。 |
| 日付 | 出来事 |
|---|
| 2026-03-23 | Linuxカーネルセキュリティチームに脆弱性を報告 |
| 2026-03-24 | 最初の確認応答を受領 |
| 2026-03-25 | パッチが提案されレビュー |
| 2026-04-01 | パッチがmainlineカーネルにコミット |
| 2026-04-22 | CVE-2026-31431が割り当てられました |
| 2026-04-29 | 公開開示 |