LUKS2 Header Malleability and Null-Cipher Abuse in Confidential VMs

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

TL;DR

  • 许多基于 Linux 的 Confidential VMs (CVMs) 在 AMD SEV-SNP 或 Intel TDX 上运行,使用 LUKS2 作为持久存储。磁盘上的 LUKS2 header 是可篡改的,对于与存储相邻的攻击者没有完整性保护。
  • 如果 header 的 data segment encryption 被设置为 null cipher(例如 “cipher_null-ecb”),cryptsetup 会接受它,guest 会在认为磁盘已加密的情况下透明地读/写明文。
  • 在 cryptsetup 2.8.0 及以前,null ciphers 可用于 keyslots;从 2.8.1 起,对于带非空密码的 keyslots 会被拒绝,但 null ciphers 仍然允许用于 volume segments。
  • Remote attestation 通常只度量 VM 的代码/配置,而不是可变的外部 LUKS headers;如果没有显式的验证/度量,拥有磁盘写入权限的攻击者可以强制明文 I/O。

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

  • A LUKS2 device starts with a header followed by encrypted data.
  • The header contains two identical copies of a binary section and a JSON metadata section, plus one or more keyslots.
  • JSON metadata defines:
    • keyslots enabled and their wrapping KDF/cipher
    • segments that describe the data area (cipher/mode)
    • digests (e.g., hash of the volume key to verify passphrases)
  • Typical secure values: keyslot KDF argon2id; keyslot and data segment encryption aes-xts-plain64.

Quickly inspect the segment cipher directly from JSON:

# Read JSON metadata and print the configured data segment cipher
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments["0"].encryption'

Root cause

  • LUKS2 headers are not authenticated against storage tampering. A host/storage attacker can rewrite the JSON metadata accepted by cryptsetup.
  • As of cryptsetup 2.8.0, headers that set a segment’s encryption to cipher_null-ecb are accepted. The null cipher ignores keys and returns plaintext.
  • Up to 2.8.0, null ciphers could also be used for keyslots (keyslot opens with any passphrase). Since 2.8.1, null ciphers are rejected for keyslots with non-empty passwords, but remain allowed for segments. Switching only the segment cipher still yields plaintext I/O post-2.8.1.

Threat model: why attestation didn’t save you by default

  • CVMs aim to ensure confidentiality, integrity, and authenticity in an untrusted host.
  • Remote attestation usually measures the VM image and launch configuration, not the mutable LUKS header living on untrusted storage.
  • If your CVM trusts an on-disk header without robust validation/attestation, a storage attacker can alter it to a null cipher and your guest will mount a plaintext volume without error.

Exploitation (storage write access required)

Preconditions:

  • 对 CVM 的 LUKS2 加密块设备具有写入权限。
  • 客户机使用磁盘上的 LUKS2 header 而没有进行健壮的验证/attestation。

Steps (high level):

  1. 读取 header JSON 并识别 data segment 定义。示例目标字段:segments[“0”].encryption。
  2. 将 data segment 的 encryption 设置为 null cipher,例如 cipher_null-ecb。保持 keyslot 参数和 digest 结构不变,以便 guest 的常用 passphrase 仍然“有效”。
  3. 更新两个 header 副本及相关的 header digests,使 header 自洽。
  4. 在下一次启动时,guest 运行 cryptsetup,使用其 passphrase 成功解锁现有的 keyslot 并挂载卷。因为 segment cipher 是 null cipher,所有读/写都是明文。

Variant (pre-2.8.1 keyslot abuse): 如果某个 keyslot 的 area.encryption 使用 null cipher,则它可以被任意 passphrase 打开。将其与 null segment cipher 结合即可在不知晓 guest 密钥的情况下无缝获得明文访问。

Robust mitigations (avoid TOCTOU with detached headers)

始终将磁盘上的 LUKS headers 视为不受信任的输入。使用 detached-header 模式,以便验证和打开使用来自受保护 RAM 的相同受信任字节:

# 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. 对整个 header 进行 MAC
  • 在使用前对整个 header 计算/验证 MAC。
  • 只有在 MAC 验证通过时才打开 volume。
  • 实际例子:Flashbots tdx-init 和 Fortanix Salmiac 采用了基于 MAC 的验证。
  1. 严格的 JSON 验证(向后兼容)
  • 导出 JSON 元数据并验证一份严格的参数 allowlist(KDF、ciphers、segment count/type、flags)。
#!/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
示例验证器(强制安全字段) ```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") ```
  1. Measure/attest the header
  • Remove random salts/digests and measure the sanitized header into TPM/TDX/SEV PCRs or KMS policy state.
  • Release decryption keys only when the measured header matches an approved, safe profile.

Operational guidance:

  • Enforce detached header + MAC or strict validation; never trust on-disk headers directly.
  • Consumers of attestation should deny pre-patch framework versions in allow-lists.

关于版本和维护者立场的说明

  • cryptsetup 维护者澄清,LUKS2 并非为在此场景下提供针对存储篡改的完整性保护而设计;null ciphers 为向后兼容而保留。
  • cryptsetup 2.8.1 (Oct 19, 2025) 在 keyslots 中带有非空密码时拒绝 null ciphers,但仍然允许 segments 使用 null ciphers。

快速检查与分流

  • 检查是否有任何 segment encryption 被设置为 a null cipher:
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
  • 在打开 volume 之前,验证 keyslot 和 segment 的算法。如果无法进行 MAC,请强制执行严格的 JSON 验证,并使用来自 protected memory 的 detached header 打开。

参考资料

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks