# LUKS2ヘッダの可変性とConfidential VMにおけるNull-Cipherの悪用

{{#include ../../banners/hacktricks-training.md}}

## TL;DR

- 多くのLinuxベースのConfidential VMs (CVMs) は AMD SEV-SNP や Intel TDX 上で動作し、永続ストレージに LUKS2 を使っている。オンディスクの LUKS2 ヘッダは可変であり、ストレージ隣接の攻撃者に対して完全性保護されていない。
- ヘッダ内のデータセグメント暗号が null cipher（例: "cipher_null-ecb"）に設定されていると、cryptsetup はそれを受け入れ、ゲストはディスクが暗号化されていると信じたまま平文の読み書きを透過的に行う。
- cryptsetup 2.8.0 以前（含む）では null ciphers を keyslots に使うことができた；2.8.1 以降は空でないパスワードを持つ keyslots では拒否されるが、null ciphers はボリュームセグメントでは引き続き許可されている。
- Remote attestation は通常 VM のコードや設定を計測し、可変な外部 LUKS ヘッダを計測しない；明示的な検証／計測が行われていなければ、ディスクへの書き込み権を持つ攻撃者は平文I/Oを強制できる。

## Background: LUKS2 on-disk format (what matters for attackers)

- LUKS2 デバイスはヘッダの後に暗号化データが続く。
- ヘッダは、2つの同一コピーのバイナリセクションと JSON メタデータセクション、さらに1つ以上の keyslots を含む。
- JSON メタデータは次を定義する:
  - 有効な keyslots とそれらのラッピング KDF/cipher
  - データ領域を記述する segments（cipher/mode）
  - digests（例: passphrases を検証するための volume key のハッシュ）
- 一般的に安全とされる値: keyslot KDF は argon2id; keyslot とデータセグメント暗号は aes-xts-plain64。

Quickly inspect the segment cipher directly from JSON:
```bash
# Read JSON metadata and print the configured data segment cipher
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments["0"].encryption'
```
## 根本原因

- LUKS2 headers はストレージの改ざんに対して認証／検証されていない。ホスト／ストレージ攻撃者は cryptsetup が受け入れる JSON メタデータを書き換えられる。
- cryptsetup 2.8.0 以降、セグメントの暗号化を cipher_null-ecb に設定するヘッダが受け入れられる。null cipher はキーを無視し平文を返す。
- 2.8.0 まで、null ciphers は keyslots にも使えた（keyslot は任意の passphrase で開く）。2.8.1 以降、非空のパスワードを持つ keyslot に対する null ciphers は拒否されるが、セグメントには引き続き許可される。セグメント暗号だけを切り替えても、2.8.1 以降でも平文 I/O が発生する。

## 脅威モデル: なぜ attestation がデフォルトであなたを救わなかったか

- CVMs は信頼されていないホスト上で機密性、完全性、真正性を確保することを目的とする。
- Remote attestation は通常 VM image と launch configuration を測定し、信頼されていないストレージ上にある可変の LUKS header は測定しない。
- CVM がオンディスクのヘッダを堅牢な検証／測定なしに信頼している場合、ストレージ攻撃者はそれを null cipher に書き換え、ゲストはエラーなく平文ボリュームをマウントしてしまう。

## Exploitation (storage write access required)

Preconditions:
- CVM の LUKS2 暗号化ブロックデバイスへの書き込みアクセス。
- ゲストがオンディスクの LUKS2 header を堅牢な検証／attestation なしに使用していること。

Steps (high level):
1) ヘッダの JSON を読み、データセグメント定義を特定する。例のターゲットフィールド: segments["0"].encryption
2) データセグメントの暗号化を null cipher（例: cipher_null-ecb）に設定する。guest の通常の passphrase が「動作」するように keyslot パラメータとダイジェスト構造はそのままにしておく。
3) ヘッダの両コピーと関連するヘッダダイジェストを更新し、ヘッダが自己整合的になるようにする。
4) 次回起動時、ゲストは cryptsetup を実行し、既存の keyslot を passphrase で正常にアンロックしてボリュームをマウントする。セグメント暗号が null cipher のため、すべての読み書きは平文となる。

Variant (pre-2.8.1 keyslot abuse): keyslot の area.encryption が null cipher の場合、任意の passphrase で開く。これを null セグメント cipher と組み合わせることで、ゲストの秘密を知らなくてもシームレスに平文アクセスできる。

## Robust mitigations (avoid TOCTOU with detached headers)

オンディスクの LUKS ヘッダは常に信頼できない入力として扱う。detached-header mode を使用し、検証とオープンが保護された RAM からの同一の信頼できるバイトを使うようにする：
```bash
# Copy header into protected memory (e.g., tmpfs) and open from there
cryptsetup luksHeaderBackup --header-backup-file /tmp/luks_header /dev/VDISK
cryptsetup open --type luks2 --header /tmp/luks_header /dev/VDISK --key-file=key.txt
```
次に、次のいずれか一つ以上を実施する:

1) ヘッダ全体のMACを行う
- 使用前にヘッダ全体に対するMACを計算/検証する。
- MACが検証されたときのみボリュームを開く。
- 実際の例: Flashbots tdx-init と Fortanix Salmiac はMACベースの検証を採用している。

2) 厳密なJSON検証（後方互換）
- JSONメタデータをダンプし、パラメータの厳格な許可リストを検証する (KDF, ciphers, segment count/type, flags).
```bash
#!/bin/bash
set -e
# Store header in confidential RAM fs
cryptsetup luksHeaderBackup --header-backup-file /tmp/luks_header $BLOCK_DEVICE
# Dump JSON metadata header to a file
cryptsetup luksDump --type luks2 --dump-json-metadata /tmp/luks_header > header.json
# Validate the header
python validate.py header.json
# Open the cryptfs using key.txt
cryptsetup open --type luks2 --header /tmp/luks_header $BLOCK_DEVICE --key-file=key.txt
```
<details>
<summary>例のバリデータ（安全なフィールドを強制する）</summary>
```python
from json import load
import sys
with open(sys.argv[1], "r") as f:
header = load(f)
if len(header["keyslots"]) != 1:
raise ValueError("Expected 1 keyslot")
if header["keyslots"]["0"]["type"] != "luks2":
raise ValueError("Expected luks2 keyslot")
if header["keyslots"]["0"]["area"]["encryption"] != "aes-xts-plain64":
raise ValueError("Expected aes-xts-plain64 encryption")
if header["keyslots"]["0"]["kdf"]["type"] != "argon2id":
raise ValueError("Expected argon2id kdf")
if len(header["tokens"]) != 0:
raise ValueError("Expected 0 tokens")
if len(header["segments"]) != 1:
raise ValueError("Expected 1 segment")
if header["segments"]["0"]["type"] != "crypt":
raise ValueError("Expected crypt segment")
if header["segments"]["0"]["encryption"] != "aes-xts-plain64":
raise ValueError("Expected aes-xts-plain64 encryption")
if "flags" in header["segments"]["0"] and header["segments"]["0"]["flags"]:
raise ValueError("Segment contains unexpected flags")
```
</details>

3) ヘッダを測定／証明する
- ランダムな salts/digests を取り除き、サニタイズされたヘッダを TPM/TDX/SEV の PCRs または KMS ポリシー状態に計測する。
- 計測したヘッダが承認済みで安全なプロファイルと一致する場合にのみ復号キーを公開する。

運用上のガイダンス:
- detached header + MAC または厳格な検証を強制し、on-disk headers を直接信頼しないこと。
- attestation の利用者は allow-lists において pre-patch framework versions を拒否すべきである。

## バージョンとメンテナの立場に関する注意

- cryptsetup のメンテナは、LUKS2 はこの設定でストレージの改ざんに対する完全性を提供するよう設計されていないと明言しており、null ciphers は後方互換性のために残されている。
- cryptsetup 2.8.1 (Oct 19, 2025) はパスワードが空でない keyslots に対する null ciphers を拒否するが、segments に対しては引き続き null ciphers を許可する。

## クイックチェックとトリアージ

- 任意の segment encryption が null cipher に設定されていないかを確認する:
```bash
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
```
- ボリュームを開く前に keyslot と segment algorithms を検証してください。MAC が使えない場合は厳格な JSON 検証を強制し、protected memory からの detached header を使用して開いてください。

## References

- [Vulnerabilities in LUKS2 disk encryption for confidential VMs (Trail of Bits)](https://blog.trailofbits.com/2025/10/30/vulnerabilities-in-luks2-disk-encryption-for-confidential-vms/)
- [cryptsetup issue #954 (null cipher acceptance and integrity considerations)](https://gitlab.com/cryptsetup/cryptsetup/-/issues/954)
- [CVE-2025-59054](https://nvd.nist.gov/vuln/detail/CVE-2025-59054)
- [CVE-2025-58356](https://nvd.nist.gov/vuln/detail/CVE-2025-58356)
- [Related context: CVE-2021-4122 (auto-recovery path silently decrypting disks)](https://www.cve.org/CVERecord?id=CVE-2021-4122)

{{#include ../../banners/hacktricks-training.md}}
