# LUKS2 Header Malleability and Null-Cipher Abuse in Confidential VMs

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

## TL;DR

- Baie Linux-gebaseerde Confidential VMs (CVMs) wat op AMD SEV-SNP of Intel TDX loop, gebruik LUKS2 vir permanente stoor.
- Die on-disk LUKS2-header is manipuleerbaar en is nie integriteits-beskerm teen aanvallers met toegang tot die stoor nie.
- As die header se data-segment enkripsie op 'n null cipher gestel is (bv. "cipher_null-ecb"), aanvaar cryptsetup dit en lees/skryf die gast stelselmatig plaintekst terwyl dit glo die skyf is enkripteer.
- Voor en insluitend cryptsetup 2.8.0 kon null ciphers vir keyslots gebruik word; sedert 2.8.1 word hulle vir keyslots met nie-leë wagwoorde verwerp, maar null ciphers bly toegelaat vir volume segments.
- Remote attestation meet gewoonlik VM-kode/konfigurasie, nie veranderlike eksterne LUKS-koppe nie; sonder eksplisiete validering/meting kan 'n aanvaller met skyf-skryftoegang plaintekst I/O afdwing.

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

- 'n LUKS2-toestel begin met 'n header gevolg deur enkripteerde data.
- Die header bevat twee identiese kopieë van 'n binêre afdeling en 'n JSON-metadata afdeling, plus een of meer keyslots.
- JSON-metadata definieer:
  - watter keyslots geaktiveer is en hul wrapping KDF/cipher
  - segments wat die data-area beskryf (cipher/mode)
  - digests (bv. hash van die volume key om passwoorde te verifieer)
- Tipiese veilige waardes: keyslot KDF argon2id; keyslot en data segment enkripsie 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'
```
## Hoof oorsaak

- LUKS2 headers word nie geverifieer teen stoor-manipulasie nie. ’n Host/stoor-aanvaller kan die JSON-metadata wat deur cryptsetup aanvaar word, herskryf.
- Vanaf cryptsetup 2.8.0 word headers wat ’n segment se encryption op cipher_null-ecb stel, aanvaar. Die null cipher ignoreer sleutels en lewer plaintext.
- Tot en met 2.8.0 kon null ciphers ook vir keyslots gebruik word (keyslot open met enige passphrase). Sedert 2.8.1 word null ciphers vir keyslots met nie-leë wagwoorde verwerp, maar bly toegelaat vir segments. Om slegs die segment cipher te verwissel gee steeds plaintext I/O ná 2.8.1.

## Dreigingsmodel: why attestation didn’t save you by default

- CVMs is ontwerp om vertrouwelikheid, integriteit en outentisiteit in ’n onbetroubare host te verseker.
- Remote attestation meet gewoonlik die VM image en launch configuration, nie die veranderlike LUKS header wat op onbetroubare stoor woon nie.
- As jou CVM ’n on-disk header vertrou sonder robuuste validasie/attestation, kan ’n stoor-aanvaller dit verander na ’n null cipher en sal jou guest ’n plaintext volume mount sonder fout.

## Eksploitasie (skryf toegang tot stoor benodig)

Voorvereistes:
- Skryftoegang tot die CVM se LUKS2-geënkripteerde bloktoestel.
- Die guest gebruik die on-disk LUKS2 header sonder robuuste validasie/attestation.

Stappe (op hoë vlak):
1) Lees die header JSON en identifiseer die data segment-definisie. Voorbeeld teikenveld: segments["0"].encryption.
2) Stel die data segment encryption op ’n null cipher, bv. cipher_null-ecb. Hou keyslot-parameters en digest-struktuur ongeskonde sodat die guest se gewone passphrase steeds “werk.”
3) Werk beide header-kopieë en geassosieerde header-digeste by sodat die header selfkonsekwent is.
4) By die volgende opstart voer die guest cryptsetup uit, ontsluit suksesvol die bestaande keyslot met sy passphrase, en mount die volume. Omdat die segment cipher ’n null cipher is, is alle lees/skryf operasies plaintext.

Variant (pre-2.8.1 keyslot misbruik): as ’n keyslot se area.encryption ’n null cipher is, open dit met enige passphrase. Kombineer met ’n null segment cipher vir naatlose plaintext toegang sonder om die guest se geheim te ken.

## Robuuste mitigasies (vermy TOCTOU met detached headers)

Behandel altyd on-disk LUKS headers as onbetroubare inset. Gebruik detached-header mode sodat validasie en opening dieselfde vertroude bytes uit beskermde RAM gebruik:
```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
```
Dwing dan een (of meer) van die volgende af:

1) Gebruik 'n MAC op die volledige header
- Bereken/verifieer 'n MAC oor die volledige header voordat dit gebruik word.
- Maak die volume slegs oop wanneer die MAC verifieer.
- Voorbeelde in die praktyk: Flashbots tdx-init en Fortanix Salmiac het MAC-gebaseerde verifikasie aangeneem.

2) Streng JSON-validasie (agtertoe verenigbaar)
- Dump JSON-metadata en valideer 'n streng allowlist van parameters (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>Voorbeeld-validator (dwing veilige velde af)</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) Meet/attesteer die header
- Verwyder random salts/digests en meet die gesaniteerde header in TPM/TDX/SEV PCRs of KMS-beleidstoestand.
- Gee ontsleutelingssleutels slegs vry wanneer die gemete header ooreenstem met 'n goedgekeurde, veilige profiel.

Operasionele riglyne:
- Handhaaf detached header + MAC of streng validering; vertrou nooit on-disk headers direk nie.
- Verbruikers van attestasie moet pre-patch framework weergawes in allow-lists weier.

## Aantekeninge oor weergawes en die instandhouer se standpunt

- cryptsetup-instandhouers het verduidelik dat LUKS2 nie ontwerp is om integriteit teen manipulering van stoormedia in hierdie konteks te bied nie; null ciphers word behou vir agtertoe-kompatibiliteit.
- cryptsetup 2.8.1 (19 Okt 2025) verwerp null ciphers vir keyslots met nie-leë wagwoorde, maar laat steeds null ciphers vir segments toe.

## Vinnige kontroles en triage

- Inspekteer of enige segment-enkripsie ingestel is op 'n null cipher:
```bash
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
```
- Verifieer keyslot- en segment-algoritmes voordat u die volume oopmaak. As u nie MAC kan doen nie, dwing streng JSON-validasie af en maak die volume oop met die detached header uit beskermde geheue.

## Verwysings

- [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}}
