技術 約12分で読めます

DirtyDecrypt(CVE-2026-31635)はRxGKでページキャッシュを書き換えるLinuxカーネルLPE

いけさん目次

TL;DR

影響 LinuxカーネルのRxRPC/RxGK経路。CONFIG_RXGK が有効なカーネルで、ローカルユーザーからrootへ上がるPoCが公開済み

対象 Fedora、Arch Linux、openSUSE Tumbleweedなど、上流に近いカーネルを追う環境が中心。Debian stableやUbuntu LTSの標準カーネルは同じ扱いにしない

確認 CVE-2026-31635のNVD本文はDoS寄りの説明だが、同じCVEレコードにDirtyDecrypt PoC参照が入った。CVE番号だけでなく、CONFIG_RXGK、修正コミット、ディストリビューションの状態を見る


V12 SecurityのPoC(実証コード)リポジトリに、DirtyDecrypt(別名DirtyCBC)が追加された。
The Hacker Newsは2026年5月19日、このPoCをLinuxカーネルCVE-2026-31635に紐づくローカル権限昇格として報じている。

このブログで前に扱ったCopy FailDirty FragFragnesiaと同じく、ディスク上のファイルを直接書き換えず、RAM上のページキャッシュを汚す系統だ。
違うのは入口で、DirtyDecryptはRxRPCのRxGKセキュリティクラスに寄っている。

RxRPCはAFSなどで使われるRPCプロトコルで、RxGKはGSSAPIベースのセキュリティクラスだ。
kernelconfig.ioのCONFIG_RXGK説明では、AFS向けのRxGKセキュリティクラスを提供し、暗号関連の設定をまとめて選択するとされている。
一般的なWebサーバーやUbuntu LTS標準カーネルの話として丸めるより、上流カーネルに近いディストリビューション、開発者ワークステーション、Kubernetes worker、共有CIランナーを先に見る話になる。

RxRPCとRxGKは何者か

RxRPCは、CMU発のAndrew File System(AFS)と、その後継であるOpenAFS・AuriStorFSで使われるRPCプロトコルだ。
Linuxカーネルには net/rxrpc/ として実装が入っていて、AF_RXRPC ソケットファミリーとしてユーザー空間から扱える。
TCPやUDPの代わりに使うものではなく、UDP上で多重化と再送・順序保証を持つAFS専用のRPC層という位置づけになる。

セキュリティクラスは、RxRPC上の通信をどう保護するかの選択肢だ。
昔から使われている rxkad はKerberos 4ベースで、Dirty Fragの記事でも入口として触れた。
rxgk(Rx GSSAPI Kerberos)はKerberos 5/GSSAPIを前提にしたより新しいセキュリティクラスで、AES-GCMやAES-CTS、HMAC-SHA系の暗号と整合性検査に対応する。
最近のAFSスタックでは rxkad から rxgk への移行が進んでいて、その実装受け皿としてLinuxカーネル側にも CONFIG_RXGK が入った経緯がある。

セキュリティクラス鍵基盤暗号カーネル設定関連バグ
rxnullなし平文CONFIG_RXRPC のみ
rxkadKerberos 4fcrypt / DESCONFIG_RXKADDirty Frag (RxRPC側)
rxgkKerberos 5 / GSSAPIAES-GCM / AES-CTS+HMACCONFIG_RXGKDirtyDecrypt

kernelconfig.ioの説明では、CONFIG_RXGK を有効にすると rxrpc 本体と一緒にAES-GCM・AES-CTS・HMAC-SHA系の依存暗号がまとめて引き込まれる。
AFSを実利用していなくても、ディストリビューションが CONFIG_AFS_FS=m と一緒に CONFIG_RXGK=y を有効化していれば、rxrpc.ko のロード経路を通じて該当コードが踏める状態になる。
DirtyDecryptの入口はこのRxGKのレスポンス検証経路で、Dirty Fragで触れた rxkad_verify_packet_1() とは別関数・別ロジックだ。
ただし「skbのfragにページキャッシュ由来のページが入り、暗号処理がin-placeで書く」という骨格は同じシリーズに収まる。

CVE本文とPoCの説明が同じ顔をしていない

NVDのCVE-2026-31635ページに載っているkernel.org由来の説明は、rxgk_verify_response() の長さチェックが逆で、 過大なRESPONSE authenticator が rxgk_decrypt_skb() へ渡り、BUG_ON(len) に到達する、という内容だ。
CVSS 3.1もCNA側で7.5 HIGH、ベクトルはネットワーク到達可能なAvailability impactとして出ている。

一方、V12のPoC READMEは「rxgk_decrypt_skb にコピーオンライトの保護がなく、rxgk pagecache writeが起きる」と書いている。
The Hacker Newsは、NVDレコードにDirtyDecrypt PoCへの参照が追加されたことから、このPoCをCVE-2026-31635として扱っている。
NVDの変更履歴でも2026年5月18日にCISA-ADPがV12のDirtyDecrypt参照を追加した。

つまり、運用側が見るべき差分は「CVE-2026-31635のCVSS 7.5だから可用性だけ」と読むか、「DirtyDecrypt PoCが同じレコードへ入ったのでLPEとして扱う」かで変わる点だ。
後者で見るなら、スコアよりもカーネル構成と修正済みパッケージを先に確認する。

RxGKのin-place復号がページキャッシュへ触る

DirtyDecryptの中心は rxgk_decrypt_skb() だ。
sk_buff はLinuxネットワークスタックのパケット表現で、線形バッファだけでなくページ断片を持てる。
Copy Fail以降の流れで何度も出てきたように、splice() やゼロコピー経路でファイル由来のページがカーネル内部の処理へ渡ると、受け側はそのページを読み取り専用の共有ページとして扱う。

そのための保護がコピーオンライトだ。
共有ページへ書き込む前に私有コピーを作れば、暗号処理の作業用書き込みが別プロセスやファイルのページキャッシュへ漏れない。
DirtyDecryptでは、RxGKの復号経路がその保護を通らず、ページキャッシュ由来のページへin-placeで書く経路になる。

flowchart TD
    A["読み取り可能なファイル"] --> B["ページキャッシュのページ"]
    B --> C["skbのfragへ参照が入る"]
    C --> D["RxGKの復号経路"]
    D --> E["コピーオンライトせず<br/>in-place書き込み"]
    E --> F["setuidバイナリなどの<br/>RAM上コピーが汚れる"]

PoC側から見た攻撃チェーンは、ローカルユーザー権限で完結する。
ユーザー名前空間や CAP_NET_ADMIN は要らず、AF_RXRPC ソケットの作成と add_key("rxrpc", ...) のRxGKトークン登録だけで入口に届く。

sequenceDiagram
    participant U as 非特権ユーザー
    participant K as カーネル
    participant PC as ページキャッシュ
    participant RX as RxRPC/RxGK経路
    U->>K: /usr/bin/su を open + read
    K->>PC: 対象バイナリがページキャッシュに載る
    U->>K: AF_RXRPC ソケット作成 + add_key("rxrpc")
    U->>K: splice/MSG_SPLICE_PAGES で送信skbへページ参照
    K->>RX: skb の frag にページキャッシュ由来ページ
    U->>RX: 細工した RESPONSE パケットを投げる
    RX->>PC: rxgk_decrypt_skb が in-place で書く
    U->>K: su を execve
    K-->>U: 改変済みキャッシュで実行 → root シェル

FragnesiaではESP-in-TCP、Dirty FragではESPとRxRPC(rxkad)、Copy FailではAF_ALGが入口だった。
DirtyDecryptはRxGKに寄った別経路なので、「Fragnesiaまで当てたから同系統は終わり」とは置けない。
ただし、対象が広くなったわけではなく、入口条件がまた少しずれた。

過去3本との位置関係

このシリーズは入口の経路を変えながら同じバグクラスを掘り続ける形になっている。
DirtyDecryptを単体で見るより、過去3本との差を並べたほうが優先度を判断しやすい。

名称CVE入口必要kernel config必要権限暫定モジュール無効化
Copy FailCVE-2026-31431splice()AF_ALG/algif_aeadCONFIG_CRYPTO_USER_API_AEAD通常ユーザーのみalgif_aead
Dirty Frag (ESP)CVE-2026-43284UDP skb frag → esp_input()CONFIG_INET_ESP + usernsCAP_NET_ADMIN(unshareで取得)esp4 / esp6
Dirty Frag (RxRPC)CVE-2026-43500rxkad_verify_packet_1()CONFIG_RXKAD通常ユーザー(add_keyrxrpc
FragnesiaCVE-2026-46300skb_try_coalesce() → ESP-in-TCPCONFIG_XFRM_ESPINTCPusernsesp4 / esp6 / espintcp
DirtyDecryptCVE-2026-31635rxgk_decrypt_skb()CONFIG_RXGK通常ユーザーrxrpc

DirtyDecryptで変わったのは、Dirty Fragの rxkad 経路が CONFIG_RXKAD 依存だったのに対し、別オプションの CONFIG_RXGK 側へ移った点だ。
Dirty Frag対策で rxrpc 自体をロード禁止にしているなら、モジュールが上がらないのでRxGK経路も塞がる。
逆に「Dirty Fragの rxkad 経路だけ気にして個別関数だけパッチした」場合、DirtyDecryptは別関数なので残る。
バグクラスとしては、SKBFL_SHARED_FRAG 起因のESP系(Fragnesia)、SKBFL_SHARED_FRAG を見ない rxkad 経路(Dirty Frag)、skb_cow_data() 相当が抜けたRxGK経路(DirtyDecrypt)、と入口ごとに別の関数で割れている。

対象はカーネル構成でかなり絞られる

The Hacker Newsは、DirtyDecryptの影響を CONFIG_RXGK が有効なディストリビューションに限ると書いている。
例として挙がっているのはFedora、Arch Linux、openSUSE Tumbleweedだ。
Moselwalの調査でも、Debian Stable、RHEL、Ubuntu LTSの標準カーネルをRxGK無効または該当コードなしとし、Fedora/Arch/Tumbleweedやmainline PPA、kernel-mlのような上流寄りカーネルを中心に挙げている。

UbuntuのCVEページを見ると、2026年5月20日時点でUbuntu 25.10系の linuxlinux-awslinux-azurelinux-gcplinux-oraclelinux-raspi などはVulnerableになっている一方、24.04 LTSや22.04 LTSの標準系はNot affectedが多い。
Debian security trackerも、bullseye、bookworm、trixieについては vulnerable code not present としている。
ここは「Linux全台」ではなく、使っているカーネル系列ごとに分かれる。

確認はこのあたりから始める。

uname -r
zcat /proc/config.gz 2>/dev/null | grep RXGK || grep RXGK /boot/config-$(uname -r) 2>/dev/null
lsmod | grep -E '^(rxrpc|afs)\b'
modprobe -n -v rxrpc

CONFIG_RXGK=y または CONFIG_RXGK=m なら、ディストリビューションのCVEページやカーネル更新履歴でCVE-2026-31635相当の修正を追う。
# CONFIG_RXGK is not set なら、DirtyDecryptのPoC条件からは外れる。
ただし、CONFIG_RXGK が無効でも、同じホストの algif_aeadesp4/esp6 の緩和は別の理由で残しておく。

なぜFedora・Arch・Tumbleweedで有効なのか

CONFIG_RXGK が「上流に近いディストリビューション」で有効になりがちな理由は、ポリシーがほぼ単純だ。
これらのディストロは原則として上流カーネルが提供する機能オプションを広めに有効化し、必要かどうかをユーザー側に委ねる方針を取っている。
Fedoraのカーネル設定方針も、デフォルトでオフにする決定にはメンテナのレビューが要る形になっていて、新規オプションは特段の理由がなければ =m で入る。

一方、Debian stableやUbuntu LTSは長期サポートと配布バイナリのサイズ・攻撃面を意識するため、上流の新しい機能オプションをそのまま有効化はしない。
RxGKは「AFSという、現代のクラウド環境では多数派ではないファイルシステムの新セキュリティクラス」なので、LTS系では is not set で残されているケースが多い。
Ubuntu 24.04 LTS・22.04 LTSや、Debian bookworm/trixieでNot affectedが並ぶのはこのためだ。

AFSを実利用しているユーザーがほとんどいないのに、rxrpc.ko は非特権ユーザーから引ける。
ここが実害の入口になる。
modprobe -n -v rxrpc がエラーにならなければ、非特権ユーザーが何らかの契機でモジュールをロードできる経路がある。
Linuxカーネルには kernel.modules_disabled sysctlや個別の modprobe.blacklist があるが、デフォルトでは多くのプロトコル系モジュールが非特権ユーザーから引ける状態にある。
AF_RXRPC ソケット作成時のautoloadがその典型例だ。

修正コミットとディストロ追跡の進め方

CVE-2026-31635のNVD説明とDirtyDecryptのPoC説明がずれているため、見るべき修正コミットも一つに絞れない。
最低でも二つ追う。

一つ目は、rxgk_verify_response() の長さチェック逆転に対する修正で、NVD本文側のDoS指摘に対応する。
二つ目は、rxgk_decrypt_skb() がページキャッシュ由来ページへin-place書きをしないよう skb_cow_data() 経由でCOWへ落とす修正で、こちらがLPE側を塞ぐ。
該当変更は net/rxrpc/rxgk_app.cnet/rxrpc/rxgk.c のあたりに入る想定で、kernel.org stableツリーへのバックポートはこのあたりで追える。

git clone --depth=200 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
cd linux
git log --oneline -- net/rxrpc/rxgk_app.c net/rxrpc/rxgk.c
git log --grep='rxgk' --since=2026-04-01 --oneline

ディストロ追跡は、CVE番号一本では足りない。
ベンダーは上流のバージョン番号を上げずに修正をバックポートするため、uname -r だけで安全判定はできない。
見るURLパターンを揃えておく。

  • Ubuntu: https://ubuntu.com/security/CVE-2026-31635
  • Debian: https://security-tracker.debian.org/tracker/CVE-2026-31635
  • Fedora: https://bodhi.fedoraproject.org/updates/?search=CVE-2026-31635
  • openSUSE/SUSE: https://www.suse.com/security/cve/CVE-2026-31635.html
  • Arch: https://security.archlinux.org/CVE-2026-31635
  • Red Hat: https://access.redhat.com/security/cve/CVE-2026-31635
  • AlmaLinux / Rocky: 上流RHELのerrata IDで検索

ベンダーが「Not affected」を出している場合は、その根拠まで読む。
AmazonがFragnesiaで espintcp 非提供を理由に影響なしを出していたように、DirtyDecryptでも CONFIG_RXGK is not set を理由に同じ判断パターンが並ぶ可能性がある。

コンテナとCIではPoCを動かして確かめない

この系統では、PoC自体がページキャッシュを汚す。
Fragnesiaの記事でも書いたが、本番で「脆弱かどうか」をPoCで試すと、setuidバイナリや認証ファイルのRAM上コピーを自分で壊す可能性がある。
DirtyDecryptも同じ発想で扱う。

Kubernetes workerやセルフホストCIランナーでは、コンテナイメージの更新では直らない。
コンテナはホストカーネルを共有するため、見るのはworker nodeのカーネル、CONFIG_RXGKrxrpc/afs のロード状態、ノードイメージの更新日だ。
信頼できないジョブやPRを動かすランナーなら、ローカル権限昇格はそのままホスト奪取の前段になる。

暫定緩和として rxrpc をロードできないようにする判断はあり得る。
既にDirty Frag/Fragnesia対策で esp4esp6rxrpc を止めているホストなら、DirtyDecrypt向けに新しい名前の設定を増やすより、既存設定が全ノードに残っているかを確認する。
AFSやRxRPCを使う環境では通信影響が出るので、サービス単位で確認してから入れる。

# 既にDirty Frag対策で入れているなら、そこに rxrpc があるかだけ確認
grep -R 'install rxrpc' /etc/modprobe.d/ 2>/dev/null

# まだ何も入れていない場合
sudo sh -c "printf 'install rxrpc /bin/false\n' > /etc/modprobe.d/dirtydecrypt.conf"
sudo rmmod rxrpc 2>/dev/null || true
sudo update-initramfs -u -k all 2>/dev/null  # Debian/Ubuntu系
sudo dracut -f 2>/dev/null                   # Fedora/RHEL系

# AFSやRxRPCを実利用しているかの簡易確認(緩和を入れる前に見る)
mount | grep -E '\bafs\b'
systemctl list-units 2>/dev/null | grep -Ei 'openafs|auristor|kafs'
ss -ulnp 2>/dev/null | grep -E '7000|7001'   # AFSのデフォルトポート帯

Kubernetesノードで配るときは、/etc/modprobe.d/ をDaemonSetやノードイメージ側で固める。
Fragnesiaの記事で書いた kubectl debug でのノード横断確認はDirtyDecryptでもそのまま流用できる。

参考