Authentifizierungsumgehung durch Capture-Replay

Beschreibung

Authentifizierungsumgehung durch Capture-Replay ist eine Schwachstelle, bei der Angreifer Netzwerkverkehr mit Authentifizierungsanmeldedaten oder Befehlen abfangen und wiederholen können, um unbefugten Zugang zu erlangen. Capture-Replay-Angriffe sind verbreitet und können ohne Kryptografie schwer zu bekämpfen sein. Sie sind eine Untergruppe von Netzwerk-Injection-Angriffen, die darauf beruhen, zuvor gesendete gültige Befehle zu beobachten und dann dieselben Befehle (möglicherweise mit leichten Modifikationen) an den Server zurückzusenden. Wenn der Server nicht zwischen frischen Anfragen und wiederholten unterscheiden kann, erreicht der Angreifer denselben authentifizierten Zustand wie der ursprüngliche legitime Benutzer.

Risiko

Replay-Angriffe stellen eine erhebliche Bedrohung für Authentifizierungssysteme dar, insbesondere für solche, die Anmeldedaten im Klartext übertragen oder einfache Hash-basierte Authentifizierung ohne Frische-Garantien verwenden. Angreifer, die in der Position sind, Netzwerkverkehr zu beobachten (durch Netzwerk-Sniffing, Man-in-the-Middle-Angriffe oder kompromittierte Infrastruktur), können Authentifizierungssequenzen erfassen und unbegrenzt wiederholen. Systeme, die MD5 oder andere Hashes für Passwortübertragung verwenden, sind besonders anfällig, da der Hash selbst zum Anmeldedatum wird. Das Risiko wird verstärkt in drahtlosen Netzwerken, gemeinsam genutzten Netzwerksegmenten und jeder Umgebung, in der Verkehrsabfangen möglich ist. Einmal erfasst, können Authentifizierungsanmeldedaten von jedem Standort aus wiederholt werden, was diese Angriffe wertvoll für persistenten Zugang macht.

Lösung

Implementieren Sie Replay-Präventionsmechanismen mit kryptografischen Techniken. Fügen Sie Sequenznummern oder Zeitstempel kombiniert mit kryptografischen Signaturen hinzu, um sicherzustellen, dass jede Nachricht nur einmal verwendet werden kann. Verwenden Sie Challenge-Response-Authentifizierung, bei der der Server für jeden Authentifizierungsversuch eine eindeutige Challenge bereitstellt. Implementieren Sie Nonces (einmal verwendete Zahlen), die in Authentifizierungsnachrichten enthalten sind und verfolgt werden, um Wiederverwendung zu verhindern. Verwenden Sie TLS für allen Authentifizierungsverkehr, um Erfassung auf Netzwerkebene zu verhindern. Für hochsensible Systeme implementieren Sie gegenseitige Authentifizierung mit Client-Zertifikaten. Erwägen Sie die Verwendung von zeitsynchronisierten Einmalpasswörtern (TOTP), die sich periodisch ändern. Wenn Sie Hashes für Authentifizierung verwenden, schließen Sie Zeitstempel und Zufallswerte in die Hash-Berechnung ein, um jede Authentifizierungsanfrage einzigartig zu machen.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Wiederholte Authentifizierungsnachrichten gewähren Angreifern denselben Zugang wie dem ursprünglichen authentifizierten Benutzer. Dies ermöglicht unbefugten Zugang zu geschützten Ressourcen, Annahme der Benutzeridentität und die Fähigkeit, Aktionen als der Opferbenutzer durchzuführen.

Beispielcode

Anfälliger Code (Python)

Die folgenden Beispiele demonstrieren Authentifizierung, die anfällig für Capture-Replay ist:

# Anfällig: Einfache Hash-basierte Authentifizierung anfällig für Replay
import hashlib
import socket

class VulnerableAuthClient:

    def authenticate(self, username, password):
        # Anfällig: Hash kann erfasst und wiederholt werden
        password_hash = hashlib.md5(password.encode()).hexdigest()

        # Anmeldedaten über Netzwerk senden
        auth_message = f"{username}:{password_hash}"
        self.socket.send(auth_message.encode())

        # Angreifer erfasst auth_message, wiederholt sie später
        response = self.socket.recv(1024)
        return response == b"AUTH_SUCCESS"

class VulnerableAuthServer:

    def handle_auth(self, client_socket):
        data = client_socket.recv(1024).decode()
        username, password_hash = data.split(':')

        # Anfällig: Derselbe Hash funktioniert immer
        stored_hash = self.get_stored_hash(username)

        if password_hash == stored_hash:
            # Wiederholter Hash wird akzeptiert!
            client_socket.send(b"AUTH_SUCCESS")
            return True

        client_socket.send(b"AUTH_FAILED")
        return False
// Anfällig: Token-basierte Authentifizierung ohne Replay-Schutz
#include <string.h>
#include <time.h>

typedef struct {
    char username[64];
    char token[128];
} AuthRequest;

int vulnerable_verify_token(AuthRequest *req) {
    // Anfällig: Token enthält keinen Zeitstempel oder Nonce
    char *stored_token = get_user_token(req->username);

    if (stored_token && strcmp(req->token, stored_token) == 0) {
        return 1;  // Erfasstes Token kann für immer wiederholt werden
    }

    return 0;
}

// Anfällig: Sitzungstoken-Generierung ohne Eindeutigkeit
char* vulnerable_generate_session(const char *username) {
    // Anfällig: Vorhersagbare, wiederholbare Sitzung
    static char session[256];

    // Kein Zeitstempel, keine Zufallskomponente
    snprintf(session, sizeof(session), "%s_session", username);

    return session;
}
// Anfällig: API-Authentifizierung ohne Replay-Schutz
public class VulnerableApiAuth {

    public boolean authenticate(HttpServletRequest request) {
        String apiKey = request.getHeader("X-API-Key");
        String signature = request.getHeader("X-Signature");

        // Anfällig: Kein Zeitstempel in Signatur
        String expectedSig = computeSignature(apiKey, request.getRequestURI());

        if (signature.equals(expectedSig)) {
            return true;  // Dieselbe Anfrage kann wiederholt werden
        }

        return false;
    }

    private String computeSignature(String apiKey, String uri) {
        // Anfällig: Deterministische Signatur ohne Zeitkomponente
        return hmacSha256(getSecret(apiKey), uri);
    }
}

Korrigierter Code (Python)

# Korrigiert: Authentifizierung mit Replay-Schutz
import hashlib
import hmac
import secrets
import time
import socket

class SecureAuthClient:

    def authenticate(self, username, password):
        # Challenge vom Server anfordern
        self.socket.send(b"REQUEST_CHALLENGE")
        challenge = self.socket.recv(1024).decode()

        # Zeitstempel einschließen
        timestamp = str(int(time.time()))

        # Antwort berechnen einschließlich Challenge und Zeitstempel
        message = f"{username}:{timestamp}:{challenge}"
        response_hash = hmac.new(
            password.encode(),
            message.encode(),
            hashlib.sha256
        ).hexdigest()

        auth_message = f"{username}:{timestamp}:{response_hash}"
        self.socket.send(auth_message.encode())

        return self.socket.recv(1024) == b"AUTH_SUCCESS"

class SecureAuthServer:

    def __init__(self):
        self.used_challenges = {}  # Verwendete Challenges verfolgen
        self.challenge_timeout = 300  # 5 Minuten

    def generate_challenge(self):
        challenge = secrets.token_hex(32)
        self.used_challenges[challenge] = time.time()
        return challenge

    def handle_auth(self, client_socket):
        data = client_socket.recv(1024).decode()

        if data == "REQUEST_CHALLENGE":
            challenge = self.generate_challenge()
            client_socket.send(challenge.encode())
            data = client_socket.recv(1024).decode()

        username, timestamp, client_hash = data.split(':')

        # Korrigiert: Zeitstempel-Frische verifizieren
        request_time = int(timestamp)
        if abs(time.time() - request_time) > self.challenge_timeout:
            client_socket.send(b"AUTH_FAILED_EXPIRED")
            return False

        # Korrigiert: Challenge wurde von uns ausgegeben und nicht wiederverwendet
        # (In der Praxis würde Challenge in der Nachricht enthalten sein)

        # Erwarteten Hash neu berechnen
        password = self.get_user_password(username)  # Sicher gespeichert
        # In echter Implementierung würde gegen gespeicherten Hash verifiziert

        # Korrigiert: Jede Authentifizierung ist einzigartig durch Zeitstempel und Challenge
        expected_message = f"{username}:{timestamp}:{self.last_challenge}"
        expected_hash = hmac.new(
            password.encode(),
            expected_message.encode(),
            hashlib.sha256
        ).hexdigest()

        if hmac.compare_digest(client_hash, expected_hash):
            client_socket.send(b"AUTH_SUCCESS")
            return True

        client_socket.send(b"AUTH_FAILED")
        return False
// Korrigiert: API-Authentifizierung mit Replay-Schutz
import javax.crypto.Mac;
import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;

public class SecureApiAuth {

    private static final long MAX_TIMESTAMP_SKEW = 300; // 5 Minuten
    private final ConcurrentHashMap<String, Long> usedNonces = new ConcurrentHashMap<>();

    public boolean authenticate(HttpServletRequest request) {
        String apiKey = request.getHeader("X-API-Key");
        String signature = request.getHeader("X-Signature");
        String timestamp = request.getHeader("X-Timestamp");
        String nonce = request.getHeader("X-Nonce");

        // Korrigiert: Zeitstempel-Frische verifizieren
        long requestTime = Long.parseLong(timestamp);
        long currentTime = Instant.now().getEpochSecond();

        if (Math.abs(currentTime - requestTime) > MAX_TIMESTAMP_SKEW) {
            return false;  // Anfrage abgelaufen
        }

        // Korrigiert: Prüfen, dass Nonce nicht verwendet wurde
        String nonceKey = apiKey + ":" + nonce;
        Long previousUse = usedNonces.putIfAbsent(nonceKey, currentTime);
        if (previousUse != null) {
            return false;  // Nonce bereits verwendet - Replay-Versuch!
        }

        // Alte Nonces periodisch bereinigen
        cleanOldNonces();

        // Korrigiert: Signatur enthält Zeitstempel und Nonce
        String message = String.format("%s:%s:%s:%s",
            timestamp, nonce, request.getMethod(), request.getRequestURI());

        String expectedSig = computeHmac(getSecret(apiKey), message);

        if (MessageDigest.isEqual(signature.getBytes(), expectedSig.getBytes())) {
            return true;
        }

        return false;
    }

    private void cleanOldNonces() {
        long cutoff = Instant.now().getEpochSecond() - MAX_TIMESTAMP_SKEW * 2;
        usedNonces.entrySet().removeIf(entry -> entry.getValue() < cutoff);
    }
}

Die Korrektur implementiert Challenge-Response-Authentifizierung mit Zeitstempeln und Nonces, um Replay von erfassten Authentifizierungsnachrichten zu verhindern.


Ausgenutzt in der Praxis

MD5-Hash-Replay-Angriffe (Verschiedene Systeme, Historisch)

CVE-2005-3435 dokumentierte MD5-Hash-Authentifizierung, die anfällig für Replay-Angriffe war. Angreifer erfassten gehashte Anmeldedaten und wiederholten sie, um unbefugten Zugang zu erlangen, ohne das ursprüngliche Passwort zu kennen.

Klartext-Protokoll-Replay (Netzwerkanwendungen, Fortlaufend)

CVE-2007-4961 dokumentierte Klartext-MD5-Übertragung kombiniert mit Replay-Schwachstelle, was Angreifern ermöglichte, Authentifizierungssequenzen zu erfassen und zu wiederholen.

Sitzungstoken-Replay (Webanwendungen, Fortlaufend)

Webanwendungen, die Sitzungstoken nicht invalidieren oder Frische-Garantien einschließen, wurden durch Sitzungserfassung und Replay von verschiedenen Standorten ausgenutzt.


Tools zum Testen/Ausnutzen

  • Wireshark — Netzwerkprotokollanalysator zum Erfassen von Authentifizierungsverkehr.

  • tcpreplay — Tool zum Wiederholen von erfasstem Netzwerkverkehr.

  • Burp Suite — Web-Sicherheitstool zum Erfassen und Wiederholen von HTTP-Anfragen.


CVE-Beispiele

  • CVE-2005-3435 — MD5-Hash-Authentifizierung anfällig für Replay-Angriffe.

  • CVE-2007-4961 — Klartext-MD5-Übertragung kombiniert mit Replay-Schwachstelle.


Referenzen

  1. MITRE Corporation. "CWE-294: Authentication Bypass by Capture-replay." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/294.html

  2. OWASP Foundation. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html

  3. NIST. "Digital Identity Guidelines: Authentication and Lifecycle Management." SP 800-63B. https://pages.nist.gov/800-63-3/sp800-63b.html