LUKS2 Header Malleability and Null-Cipher Abuse in Confidential VMs
Reading time: 7 minutes
tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the đŹ Discord group or the telegram group or follow us on Twitter đŚ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
TL;DR
- Many Linux-based Confidential VMs (CVMs) running on AMD SEV-SNP or Intel TDX use LUKS2 for persistent storage. The on-disk LUKS2 header is malleable and not integrity-protected against storage-adjacent attackers.
- If the headerâs data segment encryption is set to a null cipher (e.g., "cipher_null-ecb"), cryptsetup accepts it and the guest transparently reads/writes plaintext while believing the disk is encrypted.
- Prior to and including cryptsetup 2.8.0, null ciphers could be used for keyslots; since 2.8.1 they are rejected for keyslots with non-empty passwords, but null ciphers remain allowed for volume segments.
- Remote attestation usually measures VM code/config, not mutable external LUKS headers; without explicit validation/measurement, an attacker with disk write access can force plaintext 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/measurement, 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:
- Write access to the CVMâs LUKS2-encrypted block device.
- The guest uses the on-disk LUKS2 header without robust validation/attestation.
Steps (high level):
- Read the header JSON and identify the data segment definition. Example target field: segments["0"].encryption.
- Set the data segment encryption to a null cipher, e.g., cipher_null-ecb. Keep keyslot parameters and digest structure intact so the guestâs usual passphrase still âworks.â
- Update both header copies and associated header digests so the header is self-consistent.
- On next boot, the guest runs cryptsetup, successfully unlocks the existing keyslot with its passphrase, and mounts the volume. Because the segment cipher is a null cipher, all reads/writes are plaintext.
Variant (pre-2.8.1 keyslot abuse): if a keyslotâs area.encryption is a null cipher, it opens with any passphrase. Combine with a null segment cipher for seamless plaintext access without knowing the guest secret.
Robust mitigations (avoid TOCTOU with detached headers)
Always treat on-disk LUKS headers as untrusted input. Use detached-header mode so validation and opening use the same trusted bytes from protected 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
Then enforce one (or more) of:
-
MAC the full header
- Compute/verify a MAC over the entire header prior to use.
- Only open the volume when the MAC verifies.
- Examples in the wild: Flashbots tdx-init and Fortanix Salmiac adopted MAC-based verification.
-
Strict JSON validation (backward compatible)
- Dump JSON metadata and validate a strict allowlist of parameters (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
Example validator (enforce safe fields)
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")
- 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.
Notes on versions and maintainer position
- cryptsetup maintainers clarified that LUKS2 was not designed to provide integrity against storage tampering in this setting; null ciphers are retained for backward compatibility.
- cryptsetup 2.8.1 (Oct 19, 2025) rejects null ciphers for keyslots with non-empty passwords but still allows null ciphers for segments.
Quick checks and triage
- Inspect whether any segment encryption is set to a null cipher:
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
- Verify keyslot and segment algorithms before opening the volume. If you cannot MAC, enforce strict JSON validation and open using the detached header from protected memory.
References
- Vulnerabilities in LUKS2 disk encryption for confidential VMs (Trail of Bits)
- cryptsetup issue #954 (null cipher acceptance and integrity considerations)
- CVE-2025-59054
- CVE-2025-58356
- Related context: CVE-2021-4122 (auto-recovery path silently decrypting disks)
tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the đŹ Discord group or the telegram group or follow us on Twitter đŚ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks Cloud