Unbeabsichtigter Proxy oder Vermittler ('Confused Deputy')

Beschreibung

Unbeabsichtigter Proxy oder Vermittler, auch bekannt als das 'Confused Deputy'-Problem, ist eine Schwachstelle, bei der ein Produkt eine Anfrage, Nachricht oder Anweisung von einer vorgelagerten Komponente erhält, aber die ursprüngliche Quelle der Anfrage nicht ausreichend bewahrt, bevor sie an eine andere Entität weitergeleitet wird. Die Vermittler-Komponente handelt mit ihren eigenen Privilegien anstelle der Privilegien des ursprünglichen Anfragers, was Angreifern ermöglicht, Aktionen durchzuführen, die sie direkt nicht ausführen könnten. Dies ermöglicht Privilegieneskalation, Zugriffskontrollumgehung und Verschleierung bösartiger Aktivitäten durch Ausnutzung von Vertrauensbeziehungen zwischen Komponenten.

Risiko

Confused-Deputy-Schwachstellen ermöglichen es Angreifern, die Privilegien und den Zugriff von Vermittlersystemen zu nutzen. Server-Side Request Forgery (SSRF) erlaubt Angreifern, über Serveranfragen auf interne Ressourcen zuzugreifen. FTP-Bounce-Angriffe nutzen FTP-Server, um Systeme zu scannen oder anzugreifen, die der Angreifer nicht direkt erreichen kann. Mail-Server können ausgenutzt werden, um interne Netzwerke zu erkunden. Web-Anwendungen, die als Proxies fungieren, können unbeabsichtigt Zugriff auf administrative Schnittstellen bieten. Das Risiko wird verstärkt, wenn der Vermittler erhöhte Privilegien oder Zugriff auf sensible interne Netzwerke hat. Angreifer können auch Confused Deputies nutzen, um die Quelle ihrer Angriffe zu verbergen, was Attribution und Verteidigung erschwert.

Lösung

Erzwingen Sie starke gegenseitige Authentifizierung zwischen allen Parteien in einer Transaktionskette. Bewahren und übermitteln Sie die Identität des ursprünglichen Transaktionsinitiators durch die gesamte Verarbeitungskette - diese Identität muss unveränderlich und vom endgültigen Ziel verifizierbar sein. Implementieren Sie das Prinzip der minimalen Berechtigung, sodass Vermittler nur Zugriff haben, den sie tatsächlich benötigen. Validieren Sie, dass Anfragen für die deklarierte Quellidentität angemessen sind. Für Web-Anwendungen beschränken Sie ausgehende Anfragen auf erlaubte Ziele. Implementieren Sie Netzwerksegmentierung, um einzuschränken, auf welche internen Ressourcen Vermittler zugreifen können. Protokollieren Sie die vollständige Kette der Anfrageherkunft für Audit-Zwecke.

Häufige Auswirkungen

AuswirkungDetails
Nicht-AbstreitbarkeitUmfang: Nicht-Abstreitbarkeit

Aktivitäten verbergen - Angreifer können die Quelle von Anfragen verschleiern, indem sie sie durch vertrauenswürdige Vermittler routen, was Attribution erschwert.
ZugriffskontrolleUmfang: Zugriffskontrolle

Privilegien erlangen oder Identität annehmen - Die Privilegien und Identität des Vermittlers werden anstelle derer des ursprünglichen Anfragers verwendet, was unbefugten Zugriff ermöglicht.
IntegritätUmfang: Integrität

Unbefugten Code oder Befehle ausführen - Angreifer können den Vermittler veranlassen, Aktionen durchzuführen, die sie direkt nicht ausführen könnten, einschließlich Code-Ausführung oder Datenmodifikation.

Beispielcode

Anfälliger Code

# Anfällig: Web-Anwendung mit SSRF-Schwachstelle
from flask import Flask, request
import requests

app = Flask(__name__)

@app.route('/fetch')
def vulnerable_fetch():
    # Anfällig: Ruft jede vom Benutzer bereitgestellte URL ab
    url = request.args.get('url')

    # Anfällig: Server agiert als Proxy mit Server-Privilegien
    # Kann auf interne Ressourcen zugreifen, die Benutzer direkt nicht erreichen kann
    response = requests.get(url)

    # Angreifer stellt bereit: ?url=http://169.254.169.254/latest/meta-data/
    # Server ruft AWS-Metadaten ab (intern), gibt sie an Angreifer zurück

    # Angreifer stellt bereit: ?url=http://internal-admin.local/delete-users
    # Server führt Admin-Aktion mit Server-Zugriff durch

    return response.text

@app.route('/proxy')
def vulnerable_proxy():
    host = request.args.get('host')
    port = int(request.args.get('port', 80))
    path = request.args.get('path', '/')

    # Anfällig: Agiert als offener Proxy
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))

    # Server-Identität/IP verwendet, nicht die des ursprünglichen Anfragers
    sock.send(f"GET {path} HTTP/1.0\r\nHost: {host}\r\n\r\n".encode())

    return sock.recv(4096).decode()
// Anfällig: FTP-Server mit Bounce-Attack-Schwachstelle
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>

void vulnerable_ftp_port(int client_fd, char *port_command) {
    // Anfällig: Akzeptiert jede IP:Port vom Client
    // FTP PORT-Befehl: PORT h1,h2,h3,h4,p1,p2

    int h1, h2, h3, h4, p1, p2;
    sscanf(port_command, "PORT %d,%d,%d,%d,%d,%d",
           &h1, &h2, &h3, &h4, &p1, &p2);

    char target_ip[16];
    sprintf(target_ip, "%d.%d.%d.%d", h1, h2, h3, h4);
    int target_port = (p1 * 256) + p2;

    // Anfällig: Verbindet zu JEDER angegebenen Adresse
    // Angreifer kann dies nutzen um:
    // 1. Interne Netzwerke zu scannen (FTP-Server kann sie erreichen)
    // 2. Interne Dienste anzugreifen
    // 3. Daten durch FTP-Server zu relayieren

    data_connection = connect_to(target_ip, target_port);

    // Server-Identität verwendet, nicht die des Clients
    // Interne Systeme sehen Verbindung von vertrauenswürdigem FTP-Server
}

// Anfällig: DMA-Controller Confused Deputy
void vulnerable_dma_transfer(uint32_t src, uint32_t dst, size_t len) {
    // Anfällig: DMA arbeitet mit DMA-Controller-Privilegien
    // Nicht mit den Privilegien der anfragenden CPU

    // Mikrocontroller mit eingeschränktem Zugriff fordert Transfer an
    // DMA hat uneingeschränkten Speicherzugriff
    // Transfer erfolgt mit DMA-Berechtigungen

    dma_copy(src, dst, len);  // Umgeht CPU-Zugriffsbeschränkungen
}
// Anfällig: Mail-Server als Proxy verwendet
public class VulnerableMailServer {

    public void processVerifyCommand(String address) {
        // Anfällig: VRFY/EXPN-Befehle für Aufklärung verwendet
        // Server prüft, ob Adressen existieren, gibt Infos preis

        // Angreifer: VRFY [email protected]
        // Server: 250 Admin User <[email protected]>
        // Gibt interne Benutzerinformationen preis

        if (userExists(address)) {
            sendResponse("250 " + getUserInfo(address));
        } else {
            sendResponse("550 Benutzer nicht gefunden");
        }
    }

    public void processRelayMessage(String from, String to, String message) {
        // Anfällig: Offenes Relay
        // Server agiert als Proxy um Mail überall zuzustellen

        // Angreifer nutzt Server um:
        // 1. Spam zu senden (verbirgt Angreifer-Identität)
        // 2. Interne Mail-Server zu erkunden
        // 3. Malware von "vertrauenswürdiger" Quelle zuzustellen

        deliverMessage(to, message);  // Keine Validierung der Relay-Berechtigung
    }
}
// Anfällig: API-Gateway Confused Deputy
class VulnerableAPIGateway {

    async forwardRequest(userRequest) {
        const targetService = userRequest.headers['x-target-service'];
        const targetPath = userRequest.headers['x-target-path'];

        // Anfällig: Leitet Anfrage mit Gateway-Identität weiter
        const response = await fetch(`http://${targetService}${targetPath}`, {
            method: userRequest.method,
            headers: {
                // Anfällig: Verwendet Gateway-Service-Account-Token
                'Authorization': `Bearer ${this.gatewayToken}`,
                // Ursprüngliche Benutzer-Identität geht verloren
            },
            body: userRequest.body
        });

        // Angreifer setzt: x-target-service: admin-api.internal
        // Gateway hat Zugriff auf admin-api, Benutzer nicht
        // Anfrage gelingt mit erhöhten Gateway-Privilegien

        return response;
    }
}

Korrigierter Code

# Korrigiert: Web-Anwendung mit SSRF-Schutz
from flask import Flask, request
import requests
from urllib.parse import urlparse
import ipaddress

app = Flask(__name__)

# Korrigiert: Whitelist erlaubter Ziele
ALLOWED_HOSTS = {'api.trusted.com', 'cdn.example.com'}
BLOCKED_IP_RANGES = [
    ipaddress.ip_network('10.0.0.0/8'),
    ipaddress.ip_network('172.16.0.0/12'),
    ipaddress.ip_network('192.168.0.0/16'),
    ipaddress.ip_network('169.254.0.0/16'),  # Link-local/Metadaten
    ipaddress.ip_network('127.0.0.0/8'),     # Localhost
]

def is_safe_url(url):
    try:
        parsed = urlparse(url)

        # Korrigiert: Nur HTTPS erlauben
        if parsed.scheme != 'https':
            return False

        # Korrigiert: Gegen Whitelist prüfen
        if parsed.hostname not in ALLOWED_HOSTS:
            return False

        # Korrigiert: IP auflösen und prüfen
        import socket
        ip = socket.gethostbyname(parsed.hostname)
        ip_obj = ipaddress.ip_address(ip)

        for blocked_range in BLOCKED_IP_RANGES:
            if ip_obj in blocked_range:
                return False

        return True
    except Exception:
        return False

@app.route('/fetch')
def secure_fetch():
    url = request.args.get('url')

    # Korrigiert: URL vor Abruf validieren
    if not is_safe_url(url):
        return "Ungültige oder blockierte URL", 403

    # Korrigiert: Timeout und Größenbeschränkungen hinzufügen
    response = requests.get(url, timeout=5, stream=True)

    # Korrigiert: Antwortgröße begrenzen
    content = response.raw.read(1024 * 1024)  # 1MB max

    return content

@app.route('/proxy')
def secure_proxy():
    # Korrigiert: Proxy-Funktionalität entfernt oder stark eingeschränkt
    return "Proxy-Funktionalität nicht verfügbar", 403
// Korrigiert: FTP-Server mit Bounce-Attack-Prävention
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void secure_ftp_port(int client_fd, char *port_command,
                     struct sockaddr_in *client_addr) {
    int h1, h2, h3, h4, p1, p2;
    sscanf(port_command, "PORT %d,%d,%d,%d,%d,%d",
           &h1, &h2, &h3, &h4, &p1, &p2);

    char target_ip[16];
    sprintf(target_ip, "%d.%d.%d.%d", h1, h2, h3, h4);
    int target_port = (p1 * 256) + p2;

    // Korrigiert: Verifizieren, dass Ziel-IP mit Client-IP übereinstimmt
    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &client_addr->sin_addr, client_ip, sizeof(client_ip));

    if (strcmp(target_ip, client_ip) != 0) {
        // Korrigiert: Bounce-Angriffe ablehnen
        send_response(client_fd, "500 PORT-Adresse muss mit Client-IP übereinstimmen\r\n");
        return;
    }

    // Korrigiert: Port-Bereich einschränken (nur nicht-privilegierte Ports)
    if (target_port < 1024) {
        send_response(client_fd, "500 PORT-Nummer muss >= 1024 sein\r\n");
        return;
    }

    // Korrigiert: Nur zu verifizierter Client-Adresse verbinden
    data_connection = connect_to(target_ip, target_port);
}

// Korrigiert: DMA-Controller mit Identitätsbewahrung
typedef struct {
    uint32_t src;
    uint32_t dst;
    size_t len;
    uint32_t requester_id;  // Korrigiert: Urheber verfolgen
    uint32_t access_level;  // Korrigiert: Zugriffsrechte bewahren
} DMARequest;

int secure_dma_transfer(DMARequest *req) {
    // Korrigiert: Zugriff mit Original-Anfrager-Privilegien prüfen
    if (!check_access(req->requester_id, req->src, READ)) {
        return -EACCES;
    }
    if (!check_access(req->requester_id, req->dst, WRITE)) {
        return -EACCES;
    }

    // Korrigiert: Transfer erfolgt nur wenn Anfrager Zugriff hat
    dma_copy(req->src, req->dst, req->len);
    return 0;
}
// Korrigiert: Mail-Server mit Relay-Schutz
public class SecureMailServer {

    private Set<String> localDomains = Set.of("example.com", "mail.example.com");

    public void processVerifyCommand(String address) {
        // Korrigiert: VRFY/EXPN deaktivieren oder einschränken
        sendResponse("252 Kann Benutzer nicht verifizieren");

        // Oder: Nur für authentifizierte Benutzer erlauben
        // if (!isAuthenticated()) {
        //     sendResponse("530 Authentifizierung erforderlich");
        //     return;
        // }
    }

    public void processRelayMessage(Session session, String from,
                                     String to, String message) {
        // Korrigiert: Relay-Berechtigungen prüfen
        String recipientDomain = extractDomain(to);

        // Korrigiert: Relay nur für lokale Domains erlauben
        if (!localDomains.contains(recipientDomain)) {
            // Externer Empfänger - Authentifizierung prüfen
            if (!session.isAuthenticated()) {
                sendResponse("550 Relay verweigert");
                return;
            }

            // Korrigiert: Absender-Relay-Berechtigungen prüfen
            if (!hasRelayPermission(session.getUser(), recipientDomain)) {
                sendResponse("550 Nicht berechtigt, zu dieser Domain zu relayieren");
                return;
            }
        }

        // Korrigiert: Original-Absender für Nicht-Abstreitbarkeit protokollieren
        logMessage(session.getClientIP(), session.getUser(), from, to);

        deliverMessage(to, message);
    }
}
// Korrigiert: API-Gateway mit Identitätsbewahrung
class SecureAPIGateway {

    // Korrigiert: Whitelist interner Dienste
    static ALLOWED_SERVICES = new Set(['user-api', 'product-api', 'order-api']);

    async forwardRequest(userRequest, userIdentity) {
        const targetService = userRequest.headers['x-target-service'];
        const targetPath = userRequest.headers['x-target-path'];

        // Korrigiert: Validieren, dass Zieldienst erlaubt ist
        if (!SecureAPIGateway.ALLOWED_SERVICES.has(targetService)) {
            throw new ForbiddenError(`Dienst nicht erlaubt: ${targetService}`);
        }

        // Korrigiert: Benutzer-Autorisierung für diesen Dienst prüfen
        if (!await this.checkAuthorization(userIdentity, targetService, targetPath)) {
            throw new ForbiddenError('Benutzer nicht für diese Ressource autorisiert');
        }

        // Korrigiert: Mit bewahrter Benutzer-Identität weiterleiten
        const response = await fetch(`http://${targetService}${targetPath}`, {
            method: userRequest.method,
            headers: {
                // Korrigiert: Benutzer-Identität einschließen
                'X-Original-User': userIdentity.userId,
                'X-Original-Roles': userIdentity.roles.join(','),
                'X-Request-ID': userRequest.requestId,

                // Korrigiert: Identitäts-Assertion signieren
                'X-Identity-Signature': this.signIdentity(userIdentity),
            },
            body: userRequest.body
        });

        return response;
    }

    async checkAuthorization(userIdentity, service, path) {
        // Korrigiert: Verifizieren, dass Benutzer Zugriff auf angeforderte Ressource hat
        const permissions = await this.permissionService.getPermissions(
            userIdentity.userId
        );

        return permissions.some(p =>
            p.service === service && this.pathMatches(p.pathPattern, path)
        );
    }
}

CVE-Beispiele

  • CVE-1999-0017 - Klassischer FTP-Bounce-Angriff mit PORT-Befehl zum Scannen oder Angreifen von Systemen durch FTP-Server.
  • CVE-2005-0315 - FTP-Server erlaubte Port-Scanning, da nicht verifiziert wurde, dass PORT-Befehl-IP mit Client übereinstimmt.
  • CVE-2002-1484 - Web-Server proxyte Anfragen zu beliebigen Ports und ermöglichte Angriffe auf interne Dienste.
  • CVE-2010-1637 - Mail-Programm erlaubte internes Netzwerk-Scanning durch E-Mail-Verarbeitung.

Referenzen

  1. MITRE Corporation. "CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')." https://cwe.mitre.org/data/definitions/441.html
  2. Norm Hardy. "The Confused Deputy: (or why capabilities might have been invented)." ACM SIGOPS Operating Systems Review, 1988.
  3. OWASP. "Server Side Request Forgery." https://owasp.org/www-community/attacks/Server_Side_Request_Forgery