Uneingeschränkter Upload von Dateien mit gefährlichem Typ
Beschreibung
Uneingeschränkter Upload von Dateien mit gefährlichem Typ tritt auf, wenn Software Benutzern erlaubt, Dateien hochzuladen, ohne die Dateitypen ordnungsgemäß zu validieren, einzuschränken oder zu behandeln. Angreifer können Dateien mit gefährlichem Inhalt wie Web-Shells, Skripte oder ausführbare Dateien hochladen, die auf dem Server ausgeführt werden können. Häufige Angriffsvektoren umfassen das Hochladen von PHP-, JSP- oder ASP-Dateien auf Webserver, das Hochladen von ausführbaren Dateien, die von anderen Benutzern ausgeführt werden, oder das Hochladen von HTML/SVG-Dateien, die bösartige Skripte enthalten. Diese Schwachstelle bietet Angreifern direkte Codeausführungsmöglichkeiten auf dem Zielsystem.
Risiko
Uneingeschränkter Datei-Upload ist eine der schwerwiegendsten Web-Anwendungsschwachstellen, da sie direkt zu Remote-Codeausführung führen kann. Der Magento-Breach von 2019 kompromittierte tausende E-Commerce-Shops durch Web-Shell-Uploads. Der Equifax-Breach beinhaltete Datei-Upload-Ausnutzung während Angriffsphasen. WordPress-Plugins leiden regelmäßig unter dieser Schwachstelle und betreffen Millionen von Websites. Sobald Angreifer eine Web-Shell hochladen, erhalten sie persistenten Zugriff, um Befehle auszuführen, Daten zu stehlen, in interne Netzwerke einzudringen und Malware zu installieren. Die Auswirkung ist oft vollständige Systemkompromittierung.
Lösung
Implementieren Sie eine Whitelist erlaubter Dateierweiterungen und lehnen Sie alle anderen ab. Validieren Sie Dateiinhalte anhand von Magic Bytes/Dateisignaturen, nicht nur Erweiterungen. Benennen Sie hochgeladene Dateien um, um ursprüngliche Erweiterungen zu entfernen und Ausführung zu verhindern. Speichern Sie Uploads außerhalb des Web-Roots oder an einem Ort, der keine Skripte ausführen kann. Verwenden Sie eine separate Domain für die Auslieferung hochgeladener Inhalte. Setzen Sie ordnungsgemäße Content-Type- und Content-Disposition-Header. Implementieren Sie Dateigrößenbeschränkungen. Scannen Sie hochgeladene Dateien auf Malware. Verlassen Sie sich niemals ausschließlich auf clientseitige Validierung.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Umfang: Remote-Codeausführung Hochgeladene Skripte oder ausführbare Dateien können ausgeführt werden und geben Angreifern volle Serverkontrolle. |
| Integrität | Umfang: Systemkompromittierung Web-Shells bieten persistenten Backdoor-Zugriff für Datenmanipulation und Systemmodifikation. |
| Vertraulichkeit | Umfang: Datenpanne Serverzugriff ermöglicht Angreifern, Datenbanken, Anmeldedaten und sensible Dateien zu stehlen. |
Beispielcode + Lösungscode
Anfälliger Code
// ANFÄLLIG: Keine Dateityp-Validierung
<?php
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["file"]["name"]);
// Angreifer lädt "shell.php" hoch - wird ausgeführt!
move_uploaded_file($_FILES["file"]["tmp_name"], $target_file);
echo "Datei erfolgreich hochgeladen";
?>
// ANFÄLLIG: Nur Erweiterungsprüfung (leicht umgehbar)
<?php
$filename = $_FILES["file"]["name"];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
// Angreifer verwendet "shell.php.jpg" oder "shell.pHp"
if ($ext == "jpg" || $ext == "png") {
move_uploaded_file($_FILES["file"]["tmp_name"], "uploads/" . $filename);
}
?>
# ANFÄLLIG: Benutzerbereitgestelltem Dateinamen vertrauen
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
# Angreifer kontrolliert Dateinamen - könnte "../../../var/www/html/shell.php" sein
file.save(os.path.join(UPLOAD_DIR, file.filename))
return 'Hochgeladen'
Korrigierter Code
<?php
// SICHER: Umfassende Datei-Upload-Validierung
function secure_upload($file) {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
$max_size = 5 * 1024 * 1024; // 5 MB
// Dateigröße prüfen
if ($file['size'] > $max_size) {
throw new Exception('Datei zu groß');
}
// Erweiterung abrufen und validieren
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_extensions)) {
throw new Exception('Ungültige Dateierweiterung');
}
// MIME-Typ aus Inhalt validieren (nicht vom Benutzer bereitgestellt)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $allowed_types)) {
throw new Exception('Ungültiger Dateityp');
}
// Magic Bytes verifizieren
$magic_bytes = file_get_contents($file['tmp_name'], false, null, 0, 4);
if (!is_valid_image_magic($magic_bytes)) {
throw new Exception('Ungültiger Dateiinhalt');
}
// Zufälligen Dateinamen generieren (Original entfernen)
$new_filename = bin2hex(random_bytes(16)) . '.' . $ext;
// Außerhalb des Web-Roots speichern
$upload_path = '/var/uploads/' . $new_filename; // Nicht im Web-Root!
move_uploaded_file($file['tmp_name'], $upload_path);
return $new_filename;
}
function is_valid_image_magic($bytes) {
$signatures = [
"\xFF\xD8\xFF" => 'jpeg', // JPEG
"\x89PNG" => 'png', // PNG
"GIF87a" => 'gif', // GIF87a
"GIF89a" => 'gif', // GIF89a
];
foreach ($signatures as $sig => $type) {
if (strpos($bytes, $sig) === 0) {
return true;
}
}
return false;
}
?>
import os
import uuid
from werkzeug.utils import secure_filename
import magic # python-magic Bibliothek
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
ALLOWED_MIMES = {'image/png', 'image/jpeg', 'image/gif'}
UPLOAD_FOLDER = '/var/uploads' # Außerhalb des Web-Roots
MAX_SIZE = 5 * 1024 * 1024 # 5 MB
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload', methods=['POST'])
def upload_safe():
file = request.files['file']
# Prüfen ob Dateiname erlaubte Erweiterung hat
if not allowed_file(file.filename):
return 'Ungültiger Dateityp', 400
# Dateigröße prüfen
file.seek(0, 2) # Zum Ende springen
size = file.tell()
file.seek(0) # Zurück zum Anfang
if size > MAX_SIZE:
return 'Datei zu groß', 400
# MIME-Typ aus Inhalt validieren
mime = magic.from_buffer(file.read(2048), mime=True)
file.seek(0)
if mime not in ALLOWED_MIMES:
return 'Ungültiger Dateityp', 400
# Sicheren, zufälligen Dateinamen generieren
ext = file.filename.rsplit('.', 1)[1].lower()
new_filename = f"{uuid.uuid4().hex}.{ext}"
# An sicherem Ort speichern
filepath = os.path.join(UPLOAD_FOLDER, new_filename)
file.save(filepath)
return {'filename': new_filename}, 200
Ausgenutzt in der Praxis
Magento E-Commerce-Breach (Magento, 2019)
Kritische Datei-Upload-Schwachstelle in Magento ermöglichte Angreifern das Hochladen von Web-Shells und kompromittierte tausende Online-Shops. Kundenzahlungsdaten und persönliche Informationen wurden gestohlen.
WordPress-Plugin-Schwachstellen (WordPress, Mehrfach)
Mehrere WordPress-Plugins einschließlich WP Statistics litten unter CWE-434-Schwachstellen, die es Angreifern ermöglichten, Backdoors auf Millionen von Websites hochzuladen.
WSO2 Enterprise Integrator (WSO2, 2025)
CVE-2025-1862 in WSO2 Enterprise Integrator 6.6.0 ermöglicht Administratoren das Hochladen beliebiger Dateien über den BPEL-Uploader aufgrund unzureichender Dateinamen-Validierung.
Tools zum Testen/Ausnutzen
-
Burp Suite — Datei-Upload-Anfragen manipulieren.
-
Weevely — Web-Shell-Generierung und -Verwaltung.
-
Upload Scanner — Automatisierter Finder für Datei-Upload-Schwachstellen.
CVE-Beispiele
-
CVE-2025-1862 — WSO2 Enterprise Integrator beliebiger Datei-Upload.
-
CVE-2024-27198 — JetBrains TeamCity Authentifizierungsumgehung ermöglicht Datei-Upload.
-
CVE-2023-50164 — Apache Struts Path-Traversal Datei-Upload RCE.
Referenzen
-
MITRE. "CWE-434: Unrestricted Upload of File with Dangerous Type." https://cwe.mitre.org/data/definitions/434.html
-
OWASP. "Unrestricted File Upload." https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload