深刻度: CVSS 7.8(高)
分類: ローカル権限昇格
導入: Linux 4.14(2017年)
修正: コミット a664bf3d603d
CVE-2026-31431 は、Linux カーネルの algif_aead モジュール — カーネルのユーザー空間 Crypto API(AF_ALG)の AEAD ソケットインターフェース — におけるローカル権限昇格の脆弱性です。
この脆弱性により、権限のないユーザーが Linux カーネルの Crypto API のアドレスファミリー AF_ALG を利用して、ファイルのメモリ内コピーを保持するページキャッシュへの制御された 4 バイト書き込みを実行できます。ページキャッシュに格納された読み取り可能な実行ファイルのメモリ内コピーのコードを置き換えることで、攻撃者は、特権プロセスが後でその改変されたバージョンのファイルを実行した際に、ユーザー権限を昇格させることができます。
競合状態を勝ち取る必要があった Dirty Cow(CVE-2016-5195)とは異なり、Copy Fail は直線的なロジック欠陥です。競合、リトライ、クラッシュしやすいタイミングウィンドウなしで発動します。
コミット 72548b093ee3(「crypto: algif_aead - switch to in-place processing」)は、 内で を設定する最適化を導入し、個別の出力バッファ割り当てを排除しました。
aead_recvmsg()req->src == req->dstこの最適化により、req->src と req->dst が結合された scatterlist を指すようになりました。このため、splice() 呼び出しからのページキャッシュページが、書き込み可能な宛先 scatterlist に直接不正に連結されました。
カーネルの AEAD API は明確な出力契約を定義しています。宛先バッファは AAD || plaintext、正確に assoclen + (cryptlen - authsize) バイトを受け取ります。API のどこにもこの契約を強制するものはなく、要件として文書化もされていません。設計は、すべての AEAD アルゴリズムが意図された宛先への書き込みに限定することを前提としています。しかし、1つのアルゴリズムがその前提を満たしませんでした。
crypto/
├── algif_aead.c ← インプレース scatterlist バグ(主)
└── authencesn.c ← 範囲外スクラッチ書き込み(トリガー)
crypto/algif_aead.c — インプレース代入/* VULNERABLE — post-2017 in-place path */
static int aead_recvmsg(struct socket *sock, struct msghdr *msg,
size_t ignored, int flags)
{
/* ... */
/*
* When userspace calls splice(2) on a file, areq->tsgl contains
* page cache pages of that file. Setting dst = src hands those
* page cache pages to the crypto engine as a writable output
* buffer.
*/
aead_request_set_crypt(req,
areq->tsgl, /* src */
areq->tsgl, /* dst — identical pointer; root cause */
areq->cryptlen, iv);
}
crypto/authencesn.c — 範囲外スクラッチ書き込みauthencesn テンプレートは、IPsec の拡張シーケンス番号(ESN)サポートに使用される AEAD ラッパーです。IPsec は、64ビットのシーケンス番号を上位半分(seqno_hi、AAD のバイト 0〜3)と下位半分(seqno_lo、バイト 4〜7)に分割して使用します。ワイヤーフォーマットは seqno_lo のみを運び、seqno_hi は暗黙的です。
/* crypto/authencesn.c — scratch write past output boundary */
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
/* ... */
/*
* authencesn temporarily stores seqno_hi at offset
* (assoclen + cryptlen) in req->dst — one word past the
* legitimate output region.
*
* Under normal operation: hits kernel-owned memory.
* With in-place scatterlist: hits a page cache page.
*/
scatterwalk_map_and_copy(&seqno_hi,
req->dst,
req->assoclen + req->cryptlen, /* out-of-bounds offset */
sizeof(seqno_hi), /* 4 bytes */
1 /* write */);
/* authencesn does not restore these bytes on the failing path */
}
アップストリーム修正: コミット a664bf3d603d — 2017年の最適化をリバート。
crypto/algif_aead.c — 分離された scatterlist/* FIXED — separate src and dst */
aead_request_set_crypt(req,
areq->tsgl, /* src — may contain page cache pages; read-only */
areq->rsgl, /* dst — freshly allocated kernel buffer; writable */
areq->cryptlen, iv);
効果: ページキャッシュページは src に残り、crypto エンジンはそこから読み取りのみを行います。assoclen + cryptlen での authencesn スクラッチ書き込みは、プロセスが所有するカーネル割り当てバッファに着地します。書き込みパスからページキャッシュページに到達することはできません。
以下は、4 バイト書き込みが完全な権限昇格を達成する方法を示しています。
| ステップ | アクション |
|---|---|
| 1 | authencesn(hmac(sha256),cbc(aes)) 用に設定された AF_ALG ソケットを開く |
| 2 | 読み取り可能な setuid バイナリ(例: /usr/bin/sudo)をソケットに splice() する — これにより、そのページキャッシュページが areq->tsgl に配置される |
| 3 | スクラッチ書き込みオフセットがバイナリのキャッシュイメージ内のターゲットバイトに合致するように assoclen + cryptlen を調整する |
| 4 | recvmsg() を呼び出す — 復号パスがトリガーされ、authencesn が攻撃者が制御する 4 バイトをページキャッシュに書き込む |
| 5 | ディスク上のファイルは変更されない。整合性チェッカー、inotify、inode タイムスタンプは影響を受けない |
| 6 | その後のバイナリの実行は、すでに変更されたページキャッシュページをマッピングする — パッチされたコードが euid=0 で実行される |
書き込みは、splice されたファイルのキャッシュデータ(メモリ内)に到達し、ファイルの権限を完全にバイパスします。
./scripts/get_maintainer.pl crypto/algif_aead.c
# Herbert Xu <[email protected]>
# [email protected]
./scripts/checkpatch.pl 0001-crypto-algif_aead-revert-inplace.patch
crypto: algif_aead - restore separate src/dst scatterlists in recvmsg
Commit 72548b093ee3 ("crypto: algif_aead - switch to in-place
processing") set req->src == req->dst inside aead_recvmsg(). When
the caller uses splice(2), the source scatterlist contains page
cache pages of the spliced file. With the in-place optimization,
those same pages become the writable destination passed to the
AEAD transform.
authencesn writes 4 bytes at offset (assoclen + cryptlen) into
req->dst as scratch space for Extended Sequence Number
rearrangement and does not restore them on the error path. With
page cache pages present in dst, this write bypasses the VFS
permission layer and corrupts the in-memory image of the spliced
file without modifying the on-disk inode.
An unprivileged local user can exploit this to overwrite
arbitrary bytes in the page cache of any readable setuid binary
and escalate privileges to root.
Restore separate src and dst scatterlists so that page cache
pages are confined to the read-only source path.
Fixes: 72548b093ee3 ("crypto: algif_aead - switch to in-place processing")
Cc: [email protected] # 4.14+
Reported-by: Taeyang Lee <[email protected]>
Signed-off-by: Your Name <[email protected]>
git send-email で送信するgit send-email \
[email protected] \
[email protected] \
[email protected] \
0001-crypto-algif_aead-revert-inplace.patch
| 要素 | 目的 |
|---|---|
subsystem: file - verb phrase 件名 | 必須形式。72文字未満 |
Fixes: <sha> ("original title") | 自動安定版バックポートツールをトリガー |
Cc: [email protected] | LTS ブランチへのバックポート用にパッチをフラグ付け |
Reported-by: | 発見者への帰属 |
Signed-off-by: | Developer Certificate of Origin — 必須 |
脆弱なカーネルは、Linux 4.14 から 7.0-rc、6.18.22 より前のすべての 6.18.x、および 6.19.12 より前の 6.19.x に及びます。
暫定緩和策(ほとんどのシステムで再起動不要):
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
この回避策は、dm-crypt/LUKS、kTLS、IPsec/XFRM、OpenSSL、GnuTLS、NSS、SSH には影響しません。
xint.io/blog/copy-fail-linux-distributionsnvd.nist.gov/vuln/detail/CVE-2026-31431a664bf3d603d(mainline)72548b093ee3(Linux 4.14、2017年)