Modyfikowalność nagłówka LUKS2 i nadużycie null-cipher w Confidential VMs

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks

W skrócie

  • Wiele opartych na Linux Confidential VMs (CVMs) działających na AMD SEV-SNP lub Intel TDX używa LUKS2 do przechowywania trwałego. Nagłówek LUKS2 znajdujący się na dysku jest modyfikowalny i nie jest chroniony integralnością względem atakujących mających dostęp do warstwy pamięci.
  • Jeśli szyfrowanie segmentu danych w nagłówku ustawione jest na null cipher (np. “cipher_null-ecb”), cryptsetup to zaakceptuje i gość przezroczysto będzie czytał/zapisywał plaintext, wierząc, że dysk jest zaszyfrowany.
  • Do i włącznie z cryptsetup 2.8.0 null ciphers mogły być używane dla keyslots; od 2.8.1 są odrzucane dla keyslots z niepustymi hasłami, ale null ciphers nadal są dozwolone dla segmentów wolumenu.
  • Remote attestation zwykle mierzy kod/konfigurację VM, a nie zewnętrzne, modyfikowalne nagłówki LUKS; bez jawnej walidacji/pomiaru atakujący z możliwością zapisu na dysku może wymusić I/O w postaci plaintextu.

Tło: format LUKS2 na dysku (co ma znaczenie dla atakujących)

  • Urządzenie LUKS2 zaczyna się od nagłówka, po którym następują zaszyfrowane dane.
  • Nagłówek zawiera dwie identyczne kopie sekcji binarnej i sekcję metadanych JSON, oraz jeden lub więcej keyslots.
  • Metadane JSON definiują:
    • włączone keyslots oraz ich wrapping KDF/cipher
    • segmenty opisujące obszar danych (cipher/mode)
    • digests (np. hash klucza wolumenu do weryfikacji haseł)
  • Typowe bezpieczne wartości: keyslot KDF argon2id; szyfrowanie keyslot i segmentu danych aes-xts-plain64.

Szybko sprawdź szyfr segmentu bezpośrednio w 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'

Przyczyna źródłowa

  • LUKS2 headers nie są uwierzytelniane pod kątem manipulacji na storage. Atakujący z poziomu hosta/storage może przepisać JSON metadata akceptowany przez cryptsetup.
  • Od cryptsetup 2.8.0 akceptowane są nagłówki, które ustawiają szyfrowanie segmentu na cipher_null-ecb. Null cipher ignoruje klucze i zwraca plaintext.
  • Do wersji 2.8.0 null ciphers mogły być też używane dla keyslots (keyslot otwiera się przy dowolnym passphrase). Od 2.8.1 null ciphers są odrzucane dla keyslots z niepustymi hasłami, ale pozostają dozwolone dla segments. Zmiana tylko ciphera segmentu nadal skutkuje plaintext I/O po 2.8.1.

Model zagrożeń: dlaczego attestation domyślnie cię nie uratowało

  • CVMs mają na celu zapewnienie confidentiality, integrity i authenticity w nieufnym hoście.
  • Remote attestation zwykle mierzy obraz VM i konfigurację uruchomienia, a nie mutowalny LUKS header znajdujący się na nieufnym storage.
  • Jeśli twój CVM ufa nagłówkowi na dysku bez solidnej walidacji/measurement, atakujący na storage może go zmodyfikować na null cipher, a twój gość zamontuje wolumin w plaintext bez błędu.

Eksploatacja (wymagany dostęp zapisu do storage)

Warunki wstępne:

  • Dostęp zapisu do LUKS2-szyfrowanego urządzenia blokowego CVM.
  • Gość używa on-disk LUKS header bez solidnej walidacji/attestation.

Kroki (ogólnie):

  1. Odczytaj header JSON i zidentyfikuj definicję segmentu danych. Przykładowe pole docelowe: segments[“0”].encryption.
  2. Ustaw szyfrowanie segmentu danych na null cipher, np. cipher_null-ecb. Zachowaj parametry keyslot i strukturę digest tak, aby zwykły passphrase gościa nadal „działał.”
  3. Zaktualizuj obie kopie headera i powiązane header digesty, aby header był samospójny.
  4. Przy następnym uruchomieniu gość uruchomi cryptsetup, pomyślnie odblokuje istniejący keyslot swoim passphrase i zamontuje wolumin. Ponieważ cipher segmentu to null cipher, wszystkie odczyty/zapisy będą plaintext.

Wariant (nadużycie keyslot przed 2.8.1): jeśli area.encryption keyslota jest null cipher, otwiera się on przy dowolnym passphrase. Połącz to z null segment cipher, aby uzyskać bezproblemowy dostęp w plaintext bez znajomości sekretu gościa.

Skuteczne środki zaradcze (unikaj TOCTOU przy detached headers)

Zawsze traktuj nagłówki LUKS zapisane na dysku jako nieufne dane wejściowe. Użyj trybu detached-header, aby walidacja i otwarcie korzystały z tych samych zaufanych bajtów z chronionej pamięci 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

Wymuś jedno (lub więcej) z poniższych:

  1. MAC całego nagłówka
  • Oblicz/zweryfikuj MAC dla całego nagłówka przed użyciem.
  • Otwieraj wolumin tylko wtedy, gdy MAC zostanie zweryfikowany.
  • Przykłady w praktyce: Flashbots tdx-init i Fortanix Salmiac zastosowały weryfikację opartą na MAC.
  1. Ścisła walidacja JSON (wstecznie kompatybilna)
  • Zrzucaj metadane JSON i waliduj ścisłą listę dozwolonych parametrów (KDF, ciphers, liczba/typ segmentów, flagi).
#!/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
Przykładowy walidator (wymusza bezpieczne pola) ```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. Zmierz/atestuj nagłówek
  • Usuń losowe salts/digests i zmierz oczyszczony nagłówek do TPM/TDX/SEV PCRs lub stanu polityki KMS.
  • Wydawaj klucze odszyfrowania tylko wtedy, gdy zmierzony nagłówek pasuje do zatwierdzonego, bezpiecznego profilu.

Wytyczne operacyjne:

  • Wymuszaj detached header + MAC lub ścisłą walidację; nigdy nie ufaj bezpośrednio nagłówkom na dysku.
  • Konsumenci atestacji powinni odrzucać pre-patch wersje frameworków w allow-lists.

Notatki o wersjach i stanowisku maintainerów

  • maintainerzy cryptsetup wyjaśnili, że LUKS2 nie został zaprojektowany, aby zapewniać integralność przeciwko manipulacji pamięcią masową w tym kontekście; null ciphers są zachowane dla kompatybilności wstecznej.
  • cryptsetup 2.8.1 (Oct 19, 2025) odrzuca null ciphers dla keyslots z niepustymi hasłami, ale nadal pozwala null ciphers dla segments.

Szybkie kontrole i triage

  • Sprawdź, czy segment encryption jest ustawione na null cipher:
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
  • Zweryfikuj algorytmy keyslot i segment przed otwarciem wolumenu. Jeśli nie możesz zweryfikować MAC, wymuś ścisłą walidację JSON i otwórz, używając detached header znajdującego się w protected memory.

Źródła

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks