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

AuswirkungDetails
ZugriffskontrolleUmfang: Remote-Codeausführung

Hochgeladene Skripte oder ausführbare Dateien können ausgeführt werden und geben Angreifern volle Serverkontrolle.
IntegritätUmfang: Systemkompromittierung

Web-Shells bieten persistenten Backdoor-Zugriff für Datenmanipulation und Systemmodifikation.
VertraulichkeitUmfang: 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

  1. MITRE. "CWE-434: Unrestricted Upload of File with Dangerous Type." https://cwe.mitre.org/data/definitions/434.html

  2. OWASP. "Unrestricted File Upload." https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload