LUKS2 Header Malleability and Null-Cipher Abuse in Confidential VMs

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

TL;DR

  • Mnoge Linux-based Confidential VMs (CVMs) koje rade na AMD SEV-SNP ili Intel TDX koriste LUKS2 za persistent storage. On-disk LUKS2 header je malleable i nije zaštićen integritetom protiv napadača sa pristupom disku.
  • Ako je enkripcija data segmenta zaglavlja podešena na null cipher (npr. “cipher_null-ecb”), cryptsetup to prihvata i guest transparentno čita/piše plaintext dok veruje da je disk enkriptovan.
  • Pre i uključujući cryptsetup 2.8.0, null ciphers su mogli da se koriste za keyslots; od 2.8.1 su odbijeni za keyslots sa ne-praznim lozinkama, ali null ciphers ostaju dozvoljeni za volume segments.
  • Remote attestation obično meri VM code/config, ne mutabilna eksternih LUKS header-a; bez eksplicitne validacije/measurement-a, napadač sa pravom pisanja na disk može naterati plaintext I/O.

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

  • LUKS2 uređaj počinje zaglavljem, nakon čega sledi enkriptovani data.
  • Zaglavlje sadrži dve identične kopije binarnog dela i JSON metadata sekciju, plus jedan ili više keyslots.
  • JSON metadata definiše:
    • enabled keyslots i njihov wrapping KDF/cipher
    • segments koji opisuju data area (cipher/mode)
    • digests (npr. hash of the volume key za verifikaciju passphrases)
  • Tipične sigurne vrednosti: keyslot KDF argon2id; keyslot i enkripcija data segmenta 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'

Uzrok

  • LUKS2 headers nisu autentifikovani protiv menjanja na skladištu. Napadač sa strane hosta/skladišta može prepisati JSON metadata koje prihvata cryptsetup.
  • Počevši od cryptsetup 2.8.0, zaglavlja koja postavljaju enkripciju segmenta na cipher_null-ecb se prihvataju. null cipher ignoriše ključeve i vraća plaintext.
  • Do 2.8.0, null ciphers su se takođe mogli koristiti za keyslots (keyslot se otvara sa bilo kojim passphrase). Od 2.8.1, null ciphers se odbacuju za keyslots sa ne-praznim lozinkama, ali ostaju dozvoljeni za segments. Promena samo segment cipher i dalje daje plaintext I/O posle 2.8.1.

Model pretnje: zašto attestation nije spasilo po defaultu

  • CVMs imaju za cilj da obezbede poverljivost, integritet i autentičnost na nepouzdanom hostu.
  • Remote attestation obično meri VM image i launch configuration, a ne promenljivi LUKS header koji se nalazi na nepouzdanom skladištu.
  • Ako vaš CVM veruje on-disk headeru bez robusne validacije/measurement, napadač sa skladišta može ga izmeniti u null cipher i vaš guest će mountovati plaintext volume bez greške.

Eksploatacija (zahteva write pristup storage-u)

Preduslovi:

  • Pristup za pisanje na LUKS2-enkriptovani block device CVM-a.
  • Guest koristi on-disk LUKS2 header bez robusne validacije/attestation.

Koraci (visok nivo):

  1. Pročitajte header JSON i identifikujte definiciju data segmenta. Primer ciljnog polja: segments[“0”].encryption.
  2. Postavite enkripciju data segmenta na null cipher, npr. cipher_null-ecb. Ostavite keyslot parametre i digest strukturu neizmenjenim tako da uobičajeni passphrase gosta i dalje „radi“.
  3. Ažurirajte obe kopije headera i pripadajuće header digeste tako da je header samokonzistentan.
  4. Pri narednom boot-u, guest pokreće cryptsetup, uspešno otvara postojeći keyslot svojim passphrase-om i mountuje volumen. Pošto je segment cipher null cipher, svi čitanja/pisanja su plaintext.

Varijanta (pre-2.8.1 keyslot abuse): ako je area.encryption keyslota null cipher, on se otvara sa bilo kojim passphrase-om. Kombinujte to sa null segment cipher-om za neprimetni plaintext pristup bez poznavanja guest tajne.

Robusne mitigacije (izbegnite TOCTOU sa detached headers)

Uvek tretirajte on-disk LUKS headers kao nepouzdan ulaz. Koristite detached-header mode tako da validacija i otvaranje koriste iste pouzdane bajtove iz zaštićenog RAM-a:

# 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

Zatim primenite jedno (ili više) od:

  1. Primeni MAC na celo zaglavlje
  • Izračunaj/proveri MAC preko celog zaglavlja pre upotrebe.
  • Otvaraj volumen samo kada se MAC verifikuje.
  • Primeri u praksi: Flashbots tdx-init i Fortanix Salmiac su usvojili MAC-based verification.
  1. Stroga JSON validacija (kompatibilno unazad)
  • Dump JSON metadata i validiraj strogi allowlist parametara (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
Primer validatora (primenjuje bezbedna polja) ```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. Izmerite/potvrdite zaglavlje
  • Uklonite nasumične soli/digeste i izmerite sanitizovano zaglavlje u TPM/TDX/SEV PCR-ove ili stanje KMS politike.
  • Izdajte ključeve za dešifrovanje samo kada izmereno zaglavlje odgovara odobrenom, sigurnom profilu.

Operativne smernice:

  • Obavezno koristite odvojeno zaglavlje + MAC ili strogu validaciju; nikada direktno ne verujte zaglavljima na disku.
  • Konsumenti attestacije treba da odbace pre-patch verzije framework-a iz allow-listi.

Napomene o verzijama i stavu održavaoca

  • održavaoci cryptsetup-a pojasnili su da LUKS2 nije dizajniran da obezbedi integritet protiv manipulacije skladištem u ovom okruženju; null ciphers su zadržani radi kompatibilnosti unazad.
  • cryptsetup 2.8.1 (Oct 19, 2025) odbacuje null ciphers za keyslots sa ne-praznim lozinkama, ali i dalje dozvoljava null ciphers za segments.

Brze provere i trijaža

  • Proverite da li je enkripcija bilo kog segmenta postavljena na null cipher:
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
  • Proverite keyslot i segment algoritme pre otvaranja volumena. Ako ne možete da verifikujete MAC, primenite strogu JSON validaciju i otvorite koristeći detached header iz zaštićene memorije.

References

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks