# Variable Security `Variable Security` ist eine Kryptographie-Challenge, die eine Schwachstelle in einem benutzerdefinierten HMAC-Signierdienst ausnutzt. Der Dienst ermöglicht es Benutzern, HMAC-SHA256-Signaturen für beliebige Nachrichten unter Verwendung eines geheimen Schlüssels (der Flagge) zu generieren. Entscheidend ist, dass der Dienst eine Funktion "Key Optimization Level" bietet, mit der der Benutzer festlegen kann, wie viele Bytes des geheimen Schlüssels für das Signieren verwendet werden sollen. ## Informationsbeschaffung Uns wird eine Webinterface namens "SecureSign Enterprise" präsentiert. Es bietet ein Formular mit zwei Feldern: 1. **Message Content**: Texteingabe für die zu signierende Nachricht. 2. **Key Optimization Level**: Eine numerische Eingabe, die die Länge des zu verwendenden Schlüssels angibt. Aus der Challenge-Beschreibung ("Wir erlauben Clients, das 'Key Optimization'-Level anzupassen") und dem Formularfeld `key_len` können wir ableiten, dass die Backend-Logik etwa so aussieht: ```python # Abgeleitete Logik # key_len kommt von der Benutzereingabe current_key = SECRET_KEY[:key_len] # Schwachstelle: Verwendet nur die ersten N Bytes h = hmac.new(current_key, message.encode(), hashlib.sha256) signature = h.hexdigest() ``` Die Anwendung erstellt eine HMAC-Signatur unter Verwendung eines Teilstücks des geheimen Schlüssels, bestimmt durch die Benutzereingabe `key_len`. Dies ermöglicht es uns, Nachrichten unter Verwendung von nur den ersten $N$ Bytes der Flagge zu signieren. ## Die Schwachstelle Dieses Setup erstellt ein **Orakel**, das Informationen über den Schlüssel Byte für Byte preisgibt. Die Schwachstelle liegt in der Tatsache, dass wir genau steuern können, wie viel des unbekannten Schlüssels in der kryptographischen Operation verwendet wird. Dies ermöglicht es uns, das Problem, den gesamten Schlüssel zu finden, darauf herunterzubrechen, ihn Zeichen für Zeichen zu finden. Hier ist die Strategie: **1. Finden des ersten Bytes:** Wir kennen den Schlüssel nicht, aber wir können den Server bitten, eine Nachricht (z.B. "test") unter Verwendung von nur **1 Byte** des Schlüssels zu signieren (`key_len=1`). * Der Server berechnet `HMAC(key[0], "test")` und gibt die Signatur zurück. * Wir können dies lokal replizieren! Wir versuchen jedes mögliche Zeichen (A, B, C...) als Schlüssel. * Wir berechnen `HMAC("A", "test")`, `HMAC("B", "test")`, usw. * Wenn unsere lokale Signatur mit der Signatur des Servers übereinstimmt, wissen wir, dass wir das erste Byte des geheimen Schlüssels gefunden haben. **2. Finden des zweiten Bytes:** Jetzt, da wir das erste Byte kennen (sagen wir, es ist `{`), bitten wir den Server, "test" unter Verwendung von **2 Bytes** des Schlüssels zu signieren (`key_len=2`). * Der Server berechnet `HMAC("{?", "test")`. * Wir führen wieder Brute-Force für das unbekannte zweite Zeichen lokal durch. Wir versuchen `{A`, `{B`, `{C`... * Wir berechnen `HMAC("{A", "test")`, `HMAC("{B", "test")`... * Die Übereinstimmung enthüllt das zweite Byte. **3. Wiederholen:** Wir setzen diesen Prozess für `key_len=3`, `key_len=4` usw. fort, bis wir die gesamte Flagge wiederhergestellt haben. ## Lösung Wir können ein Skript schreiben, um diesen Byte-für-Byte-Brute-Force-Angriff zu automatisieren. ### Solver-Skript ```python import requests import hmac import hashlib import string import re # Konfiguration TARGET_URL = "http://127.0.0.1:5000" # Nach Bedarf anpassen MESSAGE = "test" MAX_LEN = 33 # Maximale Länge der Flagge (abgeleitet oder durch Ausprobieren gefunden) def get_signature(length): """Fordere Signatur vom Server mit spezifischer Schlüssellänge an.""" try: resp = requests.post(TARGET_URL, data={'message': MESSAGE, 'key_len': length}) # Extrahiere Hex-Signatur aus der HTML-Antwort match = re.search(r'([a-f0-9]{64})', resp.text) return match.group(1) if match else None except: return None def solve(): known_flag = b"" print(f"[*] Starting attack on {TARGET_URL}...") # Iteriere durch jede Byte-Position for length in range(1, MAX_LEN + 1): # 1. Hole die Zielsignatur vom Server # Diese Signatur wird unter Verwendung der echten ersten 'length' Bytes der Flagge generiert target_sig = get_signature(length) if not target_sig: print(f"[-] Failed to get signature for length {length}") break # 2. Brute-Force das nächste Zeichen found = False # Versuche alle druckbaren Zeichen for char_code in string.printable.encode(): char = bytes([char_code]) # Konstruiere unseren Rateversuch: Der Teil, den wir schon kennen + das neue Zeichen, das wir testen candidate_key = known_flag + char # Berechne HMAC lokal mit unserem Rateversuch local_sig = hmac.new(candidate_key, MESSAGE.encode(), hashlib.sha256).hexdigest() # Wenn die Signaturen übereinstimmen, ist unser Rateversuch für das neue Zeichen korrekt if local_sig == target_sig: known_flag += char print(f"[+] Byte {length}: {char.decode()} -> {known_flag.decode()}") found = True break if not found: print("[-] Character not found in printable range.") break print(f"\n[!] Final Flag: {known_flag.decode()}") if __name__ == "__main__": solve() ``` ### Ausführung Das Ausführen des Skripts stellt die Flagge Zeichen für Zeichen wieder her: ```bash $ python3 solve.py [*] Starting attack on http://127.0.0.1:5000... [+] Byte 1: { -> { [+] Byte 2: f -> {f [+] Byte 3: l -> {fl ... [+] Byte 32: } -> {flag: byte_by_byte_we_get_rich} [!] Final Flag: {flag: byte_by_byte_we_get_rich} ```