
CVE_2026_31431 用の Exploit POC
2026年4月29日に開示されたLinuxの algif_aead / authencesn ページキャッシュ・スクラッチ書き込みバグに対する検出器および概念実証LPEです。
開示レポート: https://xint.io/blog/copy-fail-linux-distributions
自分が所有しているホスト、または明示的に評価を依頼されたホストでのみ使用してください。このLPEはインメモリ状態(ページキャッシュ)を変更しますが、この手法は実際の権限昇格です。許可なくシステム上で実行することは、ほとんどの法域で違法です。
algif_aead はAEAD操作をインプレースで実行します(req->src == req->dst)。ソースデータが通常のファイルから splice() を介して供給されると、宛先scatterlistにはファイルのページキャッシュページへの参照が含まれます — つまりカーネルはそれらに書き込むことになります。その後 authencesn(hmac(sha256), cbc(aes)) アルゴリズムは、AADの seqno_lo フィールド(sendmsg で供給されたAADのバイト4〜7)の4バイトの「スクラッチ」書き込みをその宛先に対して実行し、ファイルのページキャッシュコピーを破壊します。
ディスク上のファイルは変更されないため、ディスク上に痕跡はありません。破損を観測できるのは、ページキャッシュを共有する読み取りプロセスだけです。/etc/passwd と /usr/bin/su はどちらもワールドリーダブルであるため、特権のないローカルユーザーは実行中のカーネルによるどちらかのビューを破損させることができます。
影響を受ける対象: アップストリームのリバートが適用されていない、コミット 72548b093ee3(インプレースAEAD、2017年)を含むカーネル。開示では Ubuntu 24.04 LTS、Amazon Linux 2023、RHEL 14.3、SUSE 16 が確認されましたが、基盤となるプリミティブはそれより前から存在します。
| File | 目的 |
|---|---|
test_cve_2026_31431.py | 非破壊的な検出器。一時ディレクトリ内のセンチネルファイルに対して動作し、システムバイナリには一切触れません。 |
exploit_cve_2026_31431.py | LPE。実行中ユーザーのUIDを /etc/passwd のページキャッシュ内で0に書き換え、その後 su を起動してrootシェルを取得します。 |
両スクリプトは純粋なPython 3.10+ 標準ライブラリのみで動作します。
# 1. Detect
python3 test_cve_2026_31431.py
# exit 0 = not vulnerable, 2 = vulnerable, 1 = test error
# 2. Exploit (interactive — su will prompt for your own password)
python3 exploit_cve_2026_31431.py --shell
python3 test_cve_2026_31431.py
動作内容:
AF_ALG と authencesn(hmac(sha256),cbc(aes)) アルゴリズムに到達可能であることを確認します。sendmsg+cmsg を介して8バイトのAADをseqno_loをマーカー PWND に設定してインライン送信し、センチネルのページキャッシュページの32バイトを os.splice() でAF_ALGオペレーションソケットに供給します。recv() を呼び出して復号を駆動します。認証チェックは EBADMSG で失敗しますが、スクラッチ書き込みは実行されます。出力クラス:
Precondition not met — AF_ALG または authencesn が利用できません。終了コード0。VULNERABLE to CVE-2026-31431 — マーカー PWND がスプライスされたページに書き込まれました。終了コード2。Page cache MODIFIED via in-place AEAD splice path — ページには書き込まれたが、マーカーが期待された位置に書き込まれませんでした。脆弱として扱います。終了コード2。Page cache intact — パッチ済み。終了コード0。この検出器は /usr/bin/su、/etc/passwd、または作成した一時ディレクトリ以外のファイルには一切触れず、その一時ファイルは終了時に削除されます。
python3 exploit_cve_2026_31431.py # patch only, print next steps
python3 exploit_cve_2026_31431.py --shell # patch and exec `su <user>`
動作内容:
/etc/passwd で検索し、4文字のUIDフィールドのバイトオフセットを見つけます。write4 を実行し、UIDを 0000 に置き換えます。pwd.getpwnam(user) を呼び出し、libc がUID 0を報告することを確認します。--shell 指定時は、execvp("su", ["su", user]) を実行します。自分のパスワードを入力します。PAM は /etc/shadow(未変更)に対して検証し、その後 setuid(getpwnam(user).pw_uid) が0になります。write4 を拡張してください。/etc/passwd の読み取りをマスクするNSSキャッシュデーモン(nscd、sssd、systemd-userdbd)が存在しないこと。パッチ適用後に getpwnam が実際のUIDを返す場合は、キャッシュを再起動するかバイパスするか、別のユーザーを選択してください。/etc/passwd のページが、パッチ適用から su 実行までの間キャッシュに残っている必要があります。実際には、通常のメモリプレッシャー下のあらゆるシステムで信頼できます。ディスク上の /etc/passwd は変更されません。
ドライラン(--shell なしの exploit_cve_2026_31431.py)は、終了時に POSIX_FADV_DONTNEED を介して破損したページを自動的にエビクトするため、UID→名前のルックアップは直ちに正常に戻ります。
--shell 実行後は、ページをクリアするまで破損したまま残ります。破損している間、UID 1000 → 名前を解決するもの(例: ls、ファイルマネージャー、scp/sftpの所有権チェック)は失敗するか、数値IDを表示します。クリアするには:
# unprivileged - request page-cache eviction for /etc/passwd:
python3 -c "import os; fd=os.open('/etc/passwd', os.O_RDONLY); \
os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED); os.close(fd)"
# from the root shell:
echo 3 > /proc/sys/vm/drop_caches
再起動してもクリアされます。
write4 の仕組みsendmsg([8-byte AAD], cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
flags=MSG_MORE)
splice(target_fd, pipe_w, 32, offset_src=file_offset)
splice(pipe_r, op_fd, 32)
recv(op_fd) # EBADMSG; scratch write has already landed
AADの位置4〜7(seqno_lo)の4バイトは、authencesn によって宛先scatterlistに書き込まれます。このコードパスでは、その宛先は target_fd からスプライスしたページキャッシュページです。ページ内の書き込み先オフセットは、splice() に渡した offset_src に対応します。
パッチ済みカーネルがお使いのディストリビューションに届くまで:
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
sudo rmmod algif_aead 2>/dev/null
適用後、test_cve_2026_31431.py は Precondition not met を報告し、終了コード0を返すはずです。
アップストリームの修正は、インプレースAEAD操作をアウトオブプレースに戻し、ページキャッシュページが書き込み可能なscatterlistに含まれないようにします。