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
| Auswirkung | Details |
|---|---|
| Nicht-Abstreitbarkeit | Umfang: Nicht-Abstreitbarkeit Aktivitäten verbergen - Angreifer können die Quelle von Anfragen verschleiern, indem sie sie durch vertrauenswürdige Vermittler routen, was Attribution erschwert. |
| Zugriffskontrolle | Umfang: 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ät | Umfang: 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
- MITRE Corporation. "CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')." https://cwe.mitre.org/data/definitions/441.html
- Norm Hardy. "The Confused Deputy: (or why capabilities might have been invented)." ACM SIGOPS Operating Systems Review, 1988.
- OWASP. "Server Side Request Forgery." https://owasp.org/www-community/attacks/Server_Side_Request_Forgery