
教育向けに書き直したCopy Fail PoC(CVE-2026-31431)— algif_aeadのインプレース暗号化とsplice()ページキャッシュ書き込みを介したLinuxカーネルLPE
Theori/Xintによる概念実証コードの教育目的の書き直しです。これはLinuxカーネルのalgif_aead暗号ソケットインターフェースにおけるローカル権限昇格の脆弱性です。4.14(2017年7月)から6.18.21までのすべてのメインラインカーネル、および6.19.11までの6.19安定版ブランチに影響します。
教育目的および許可されたセキュリティ研究のみを対象としています。 所有していないシステム、または明示的な書面によるテスト許可を得ていないシステムでは実行しないでください。脆弱なホストで実行すると、権限のないローカルユーザーが即座にrootアクセスを取得できます。著者は誤用について一切の責任を負いません。
Linuxカーネルはカーネル3.2以降、AF_ALGソケットファミリー(<linux/if_alg.h>)を介してユーザー空間にカーネル内暗号APIを公開しています。ユーザー空間はSOCK_SEQPACKETソケットを開き、アルゴリズムテンプレート(例:"aead" / "authencesn(hmac(sha256),cbc(aes))")にバインドし、暗号化または復号化のためにデータを送信します。
2017年、カーネルがソースとデスティネーションが同じ基盤ページを共有していることを検出した場合に、AEADアルゴリズムがデータをインプレースで暗号化または復号化できるようにするパフォーマンス最適化がマージされました。これにより、各操作での冗長なメモリコピーが回避されます。
authencesnテンプレート(認証付き暗号化に拡張シーケンス番号サポートを追加するもの)には、復号化中のスクラッチ書き込みが含まれています。認証チェックが実行される前に、操作のデータバッファ内の特定のオフセットにESNバイトを書き込みます。操作のソースページがsplice()を介して通常のファイルのページキャッシュから取得され、インプレース最適化が有効な場合、このスクラッチ書き込みはそれらのキャッシュされたページに直接戻ります。
カーネルのページキャッシュは共有マッピングであるため(同じファイルを開くプロセスは同じ物理ページを参照します)、権限のないユーザーは以下を行うことができます:
/usr/bin/su)をO_RDONLYで開きます。AF_ALG操作ソケットにスプライスします。authencesn復号化をトリガーします。ESNスクラッチ書き込みが、攻撃者が制御するバイトで制御されたオフセットのページを破壊します。ディスク上のファイルのチェックサムを比較するオフホスト整合性スキャナー(AIDE、Tripwire、IMA/EVM)は、この攻撃を完全に検知できません。書き込みはページキャッシュの退避または再起動後に消え、ディスク上にフォレンジックの痕跡を残しません。
/usr/bin/su(setuid rootバイナリ)をターゲットにし、PAM認証チェックをガードするブランチを上書きすることで、攻撃者はパスワードなしのrootシェルを取得します。
このバグを導入した最適化コミットは4.14開発サイクル(2017年7月)にマージされました。上記の範囲でCONFIG_CRYPTO_USER_API_AEAD=yおよびCONFIG_CRYPTO_AUTHENC=y(両方とも一般的なデフォルト)でコンパイルされたカーネルは脆弱です。
正確なアドバイザリの状態については、ディストリビューションのセキュリティトラッカーを確認してください。
os.spliceはPython 3.10で追加されました)/usr/bin/su、setuidのためワールドリーダブル)# カーネルに触れずに書き込まれる内容を表示(安全):
python3 copy_fail_exploit.py --dry-run
# /usr/bin/suページキャッシュをパッチ(脆弱なカーネルが必要):
# 警告:脆弱なカーネルが必要 — 所有するシステムでのみ使用してください
python3 copy_fail_exploit.py
# パッチして即座にrootに昇格:
python3 copy_fail_exploit.py --spawn-shell
# カスタムターゲットバイナリとペイロードを使用:
python3 copy_fail_exploit.py --target /usr/bin/sudo --payload-file ./custom_patch.bin
# 詳細出力(各4バイト書き込みを表示):
python3 copy_fail_exploit.py --verbose --dry-run
デフォルトでは--spawn-shellはオフです。パッチ後、スクリプトは以下を出力します:
[+] Patch applied to page cache of '/usr/bin/su' (disk unchanged).
Run `su` to escalate, or re-run with --spawn-shell.
To restore: echo 3 | sudo tee /proc/sys/vm/drop_caches
シェルスポーンには--spawn-shellでオプトインする必要があります。これは意図的です。結果を実行する前に、エクスプロイトが何を行ったかを理解することを強制します。
プリミティブの設定。
エクスプロイトはAF_ALGソケットを開き、ダミーの全ゼロキーでauthencesn(hmac(sha256),cbc(aes))テンプレートにバインドします。次に、ALG_SET_AEAD_AUTHSIZEを介して認証タグサイズを4バイトに設定します。制御ソケットでのaccept()は、個々の復号化リクエストを送信できる操作ソケットを生成します。
ページキャッシュ書き込みのトリガー。
パッチペイロードの各4バイトチャンクについて、エクスプロイトはMSG_MOREを設定して操作ソケットでsendmsg()を呼び出し、8バイトの関連データ(AAD)を渡します:4バイトのフィラーとそれに続く4バイトのペイロードです。MSG_MOREは、より多くのデータが到着するまで操作を保持するようカーネルに指示します。次にパイプを作成し、2つのsplice()呼び出し — file_fd → pipe → op_sock — を発行して、ファイル自身のページキャッシュページを復号化操作への入力として提供します。最後にrecv()が呼び出されると、カーネルはauthencesn復号化を実行します。インプレース最適化が発動し(ソースとデスティネーションが同じページ)、ESNスクラッチ書き込みがターゲットオフセットのキャッシュページにペイロードバイトをコピーします。認証タグを検証できないため、recv()はEBADMSGを返します — これは予期されており無害です。書き込みはすでに発生しています。
バイナリのパッチ。
埋め込まれたペイロード(Theori PoCからそのまま取得)は、標準のutil-linuxビルドの/usr/bin/suのブランチ命令を上書きし、PAM認証を強制する条件付きジャンプを無条件のフォールスルーに変換します。パッチ後、パスワードなしでsuを実行すると成功し、rootシェルがドロップされます。ディスク上のバイナリは変更されません。ページキャッシュエントリをドロップすると(echo 3 > /proc/sys/vm/drop_caches)、変更は即座に元に戻ります。
より深い技術的分析については、以下の参考文献、特にXintのブログ記事とTheoriのPoCリポジトリを参照してください。
- rule: AF_ALG socket opened by unprivileged process
desc: >
CVE-2026-31431 — an unprivileged process opened an AF_ALG (family 38)
socket, which is the first step of the Copy Fail exploit.
condition: >
evt.type = socket and
evt.arg.domain = 38 and
not user.uid = 0
output: >
AF_ALG socket opened (user=%user.name uid=%user.uid
pid=%proc.pid comm=%proc.name)
priority: WARNING
tags: [host, network, privilege_escalation, CVE-2026-31431]
rule CopyFail_CVE_2026_31431 {
meta:
description = "Detects Copy Fail exploit script in memory or on disk"
cve = "CVE-2026-31431"
strings:
$template = "authencesn(hmac(sha256),cbc(aes))" ascii
$splice = "os.splice" ascii
$aflag = "AF_ALG" ascii
condition:
all of them
}
/etc/audit/rules.d/cve-2026-31431.rulesに追加:
-a always,exit -F arch=b64 -S socket -F a0=38 -k cve_2026_31431_afalg
次にログを監視:
ausearch -k cve_2026_31431_afalg --interpret
1. カーネルパッチを適用(推奨) カーネル6.18.22、6.19.12、7.0-rc7以降にアップグレードするか、ディストリビューションのセキュリティアップデートを適用します。
2. ビルド時にCONFIG_CRYPTO_USER_API_AEADを無効化 独自のカーネルをビルドする場合は、以下を設定します:
# CONFIG_CRYPTO_USER_API_AEAD is not set
これによりAF_ALG AEADインターフェース全体が削除され、攻撃対象領域が排除されます。ほとんどの組み込み/ハードニングされたデプロイメントでは必要ありません。
3. seccomp — AF_ALGソケットファミリーを拒否
機密プロセス(またはLSMポリシーを介してシステム全体)に、socket(AF_ALG, ...)を拒否するseccompフィルターを追加します:
// Deny socket() when domain == AF_ALG (38)
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 38, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EACCES),
4. カーネルハードニングによるページキャッシュ権限の削減
vm.unprivileged_userfaultfd=0および同様のチューナブルは、ユーザーがアクセス可能なカーネルメモリインターフェースの攻撃対象領域を削減しますが、このCVEを直接ブロックするものではありません。
エクスプロイトはメモリ内のページキャッシュのみを変更します。ディスク上のバイナリは決して変更されません。メモリ内の元のファイルコンテンツを復元するには、カーネルのページキャッシュをドロップします:
echo 3 | sudo tee /proc/sys/vm/drop_caches
これにより、キャッシュからすべてのクリーンページが退避されます。/usr/bin/su(またはパッチされたバイナリ)の次の読み取りでは、ディスクから元のバイトが再ロードされます。
再起動でもページキャッシュは完全にクリアされます。
MIT — LICENSEを参照。
カスタムの「教育目的のみ」ライセンスは、OSI/SPDX識別子として認識されるライセンスが存在しないため、MITが選択されました。MITは寛容で、法的に十分に理解されており、責任ある開示リポジトリと互換性があります。教育目的は、モジュールのdocstring、このREADMEの免責事項セクション、および--help出力で表現されています — 法的に疑わしいカスタムライセンス条項ではありません。
| ブランチ | 最初の脆弱バージョン | 最後の脆弱バージョン | 修正バージョン |
|---|
| mainline | 4.14 | 6.18.21 | 6.18.22 |
| 6.19 stable | 6.19.0 | 6.19.11 | 6.19.12 |
| 7.0-rc | 7.0-rc1 | 7.0-rc6 | 7.0-rc7 |
| ディストリビューション | 出荷カーネル(おおよそ) | パッチ適用済み? |
|---|
| Debian 12 (Bookworm) | 6.1.x | はい(バックポート) |
| Debian 13 (Trixie) | 6.12.x / 6.14.x | はい(バックポート) |
| Ubuntu 24.04 LTS | 6.8.x | はい(USN-7xxx) |
| Ubuntu 24.10 | 6.11.x | はい |
| RHEL 9.x | 5.14.x(リベース済み) | はい(RHSA) |
| Alpine 3.20 | 6.6.x | はい(バックポート) |
| Arch Linux | ローリング(現在≥ 6.18.22) | はい |
| Kali Linux 2026.1 | 6.18.12 | 脆弱 |
| Kali Linux 2026.2+ | 6.18.22+ | 修正済み |
| Linux Mint 22 | 6.8.x(Ubuntuベース) | はい(Ubuntu USN) |