Files
hacktricks-cloud/src/pentesting-cloud/confidential-computing/luks2-header-malleability-null-cipher-abuse.md

12 KiB

LUKS2 हेडर की मलेएबिलिटी और Confidential VMs में Null-Cipher दुरुपयोग

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

सारांश

  • कई Linux-आधारित Confidential VMs (CVMs) जो AMD SEV-SNP या Intel TDX पर चलती हैं, persistent storage के लिए LUKS2 का उपयोग करती हैं। डिस्क पर मौजूद LUKS2 हेडर मलेएबल है और storage-adjacent attackers के खिलाफ integrity-protected नहीं है।
  • यदि हेडर के डेटा सेगमेंट एन्क्रिप्शन को null cipher (जैसे "cipher_null-ecb") पर सेट किया गया है, तो cryptsetup इसे स्वीकार कर लेता है और guest पारदर्शी रूप से plaintext पढ़/लिख/पढ़ता/लिखता है जबकि वह मानता है कि डिस्क एन्क्रिप्टेड है।
  • cryptsetup 2.8.0 तक (और इसे शामिल करते हुए), null ciphers keyslots के लिए उपयोग किए जा सकते थे; 2.8.1 के बाद non-empty passwords वाले keyslots के लिए इन्हें reject कर दिया गया है, पर null ciphers अभी भी volume segments के लिए अनुमति रहती है।
  • Remote attestation आमतौर पर VM के code/config को measure करती है, न कि mutable external LUKS headers; explicit validation/measurement के बिना, disk write access वाला एक attacker plaintext I/O जबरदस्ती कर सकता है।

पृष्ठभूमि: LUKS2 on-disk फॉर्मेट (attackers के लिए क्या मायने रखता है)

  • एक LUKS2 डिवाइस एक हेडर के साथ शुरू होता है और उसके बाद encrypted data रहती है।
  • हेडर में binary section की दो identical copies और एक JSON metadata section होता है, साथ ही एक या अधिक keyslots होते हैं।
  • JSON metadata परिभाषित करता है:
    • सक्षम keyslots और उनके wrapping KDF/cipher
    • segments जो data area का वर्णन करते हैं (cipher/mode)
    • digests (जैसे, volume key का hash जो passphrases को verify करने के लिए)
  • आम तौर पर सुरक्षित मान: keyslot KDF argon2id; keyslot और data segment एन्क्रिप्शन aes-xts-plain64।

तेज़ी से JSON से सीधे segment cipher का निरीक्षण करें:

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

मूल कारण

  • LUKS2 headers स्टोरेज टेम्परिंग के खिलाफ प्रमाणीकृत नहीं हैं। एक host/storage attacker cryptsetup द्वारा स्वीकार किए गए JSON metadata को फिर से लिख सकता है।
  • cryptsetup 2.8.0 से, ऐसे headers जिन्हें किसी segment की encryption cipher_null-ecb पर सेट करते हैं स्वीकार किए जाते हैं। null cipher keys को अनदेखा करता है और plaintext लौटाता है।
  • 2.8.0 तक, null ciphers को keyslots के लिए भी उपयोग किया जा सकता था (keyslot किसी भी passphrase से खुल जाता था)। 2.8.1 के बाद, non-empty passwords वाले keyslots के लिए null ciphers अस्वीकार कर दिए जाते हैं, पर वे segments के लिए अभी भी अनुमति देते हैं। केवल segment cipher बदलने से भी 2.8.1 के बाद plaintext I/O मिलता है।

Threat model: क्यों attestation ने डिफ़ॉल्ट रूप से आपकी रक्षा नहीं की

  • CVMs का उद्देश्य untrusted host में confidentiality, integrity, और authenticity सुनिश्चित करना है।
  • Remote attestation आमतौर पर VM image और launch configuration को मापता है, न कि untrusted storage पर मौजूद mutable LUKS header को।
  • यदि आपका CVM किसी on-disk header को robust validation/measurement के बिना trust करता है, तो एक storage attacker उसे null cipher में बदल सकता है और आपका guest बिना त्रुटि के एक plaintext volume को mount कर लेगा।

Exploitation (storage write access required)

पूर्व शर्तें:

  • CVM के LUKS2-encrypted block device पर write access।
  • Guest on-disk LUKS2 header को robust validation/attestation के बिना उपयोग करता है।

चरण (उच्च-स्तर):

  1. header JSON पढ़ें और data segment definition की पहचान करें। उदाहरण target field: segments["0"].encryption।
  2. data segment encryption को null cipher पर सेट करें, उदाहरण के लिए cipher_null-ecb। keyslot parameters और digest structure को यथावत रखें ताकि guest का सामान्य passphrase अभी भी "काम" करे।
  3. दोनों header copies और संबंधित header digests को अपडेट करें ताकि header self-consistent हो।
  4. अगले बूट पर guest cryptsetup चलाता है, मौजूदा keyslot को अपने passphrase से सफलतापूर्वक unlock करता है, और volume को mount कर लेता है। चूँकि segment cipher null cipher है, सभी reads/writes plaintext होंगे।

Variant (pre-2.8.1 keyslot abuse): अगर किसी keyslot का area.encryption null cipher है, तो वह किसी भी passphrase से खुल जाता है। इसे null segment cipher के साथ मिलाकर guest secret जाने बिना seamless plaintext access प्राप्त किया जा सकता है।

Robust mitigations (avoid TOCTOU with detached headers)

हमेशा on-disk LUKS headers को untrusted input मानें। Use detached-header mode ताकि validation और opening एक ही 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

फिर इनमें से एक (या अधिक) लागू करें:

  1. MAC the full header
  • पूरे हेडर पर MAC की गणना/सत्यापन करें इस्तेमाल से पहले।
  • केवल तभी volume खोलें जब MAC सत्यापित हो।
  • वास्तविक दुनिया में उदाहरण: Flashbots tdx-init और Fortanix Salmiac ने MAC-आधारित सत्यापन अपनाया।
  1. Strict JSON validation (backward compatible)
  • JSON मेटाडेटा को dump करें और पैरामीटरों के एक कठोर 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. हेडर को मापें/attest करें
  • रैंडम salts/digests हटाएँ और sanitized header को TPM/TDX/SEV PCRs या KMS policy state में मापें/attest करें।
  • केवल तभी decryption keys जारी करें जब measured header किसी approved, safe profile से मेल खाता हो।

Operational guidance:

  • detached header + MAC या कड़ी validation लागू करें; on-disk headers पर कभी भी सीधे भरोसा न करें।
  • attestation के उपभोक्ताओं को allow-lists में pre-patch framework versions को अस्वीकार करना चाहिए।

Notes on versions and maintainer position

  • cryptsetup के मेंटेनरों ने स्पष्ट किया कि इस सेटिंग में LUKS2 को storage tampering के खिलाफ integrity प्रदान करने के लिए डिज़ाइन नहीं किया गया था; backward compatibility के लिए null ciphers बरकरार रखे गए हैं।
  • cryptsetup 2.8.1 (Oct 19, 2025) non-empty passwords वाले keyslots के लिए null ciphers को reject करता है लेकिन segments के लिए null ciphers अभी भी allow करता है।

त्वरित जांच और ट्रायज

  • जाँचें कि क्या किसी भी segment encryption को null cipher पर सेट किया गया है:
cryptsetup luksDump --type luks2 --dump-json-metadata /dev/VDISK \
| jq -r '.segments | to_entries[] | "segment=" + .key + ", enc=" + .value.encryption'
  • वॉल्यूम खोलने से पहले keyslot और segment algorithms की जाँच करें। यदि आप MAC नहीं कर सकते, तो कठोर JSON मान्यकरण लागू करें और protected memory से detached header का उपयोग करके खोलें।

References

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