flawopen.com/path-traversal/C-cpp

● CWE-918 · Hoch
Sicherheitsforschung · FlawOpen

Path Traversal in C/C++

How snprintf file path construction allows directory escapes in C/C++, and how to enforce containment using realpath() and strncmp.

💡 Einfache Erklärung (ELI5)

Stellen Sie sich ein Hotelschloss vor, das eigentlich nur Zimmer im zweiten Stock öffnen soll. Wenn ein Gast am Tastenfeld '../../master-safe' eingibt, verlässt das Schloss den Flur und öffnet den Haupttresor des Hotelmanagers.

Kernkonzepte & Begriffe

Web Application Security
Kernkomponente der Architektur, die von CWE-918 betroffen ist.
CWE-918
Standardmäßige Common Weakness Enumeration (CWE)-Klassifizierung für path-traversal-c-cpp.
Defense-in-Depth
Mehrschichtige technische Verifikation und Laufzeit-Grenzenisolation.

Schritt-für-Schritt Angriffsablauf

Step 1

Eingabe des Client-Dateipfads

Ein Endpunkt akzeptiert Dateinamen oder Ressourcenbezeichner direkt über einen HTTP-Anfrageparameter.

Step 2

Injektion von Verzeichnis-Traversierungssequenzen

Der Angreifer schleust relative Pfadsequenzen wie '../', '..%2f' oder absolute Pfadüberschreibungen ein.

Step 3

Umgehung der Verzeichnisgrenze

Das Backend verbindet die Eingabe mit dem Basispfad, ohne kanonische Pfade aufzulösen oder Grenzen zu prüfen.

Step 4

Unbefugtes Lesen oder Überschreiben von Dateien

Das System öffnet und übermittelt vertrauliche Systemdateien (z. B. /etc/passwd oder Konfigurationsgeheimnisse).

Quellcode: Verwundbar vs. Sicher

✕ VERWUNDBARE IMPLEMENTIERUNG
// VULNERABLE: Direct snprintf and open without realpath check
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int serve_user_file(const char *user_filename) {
    char full_path[1024];
    const char *base_dir = "/var/app/public/files";

    // Attacker input: "../../../../etc/shadow"
    snprintf(full_path, sizeof(full_path), "%s/%s", base_dir, user_filename);

    // Directly opens system shadow file!
    int fd = open(full_path, O_RDONLY);
    return fd;
}
✓ GEHÄRTETER SICHERHEITS-PATCH
// HARDENED: Canonicalize with realpath and verify directory prefix bounds
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>

int serve_user_file(const char *user_filename) {
    const char *base_dir = "/var/app/public/files";
    char base_canonical[PATH_MAX];
    char raw_path[PATH_MAX];
    char target_canonical[PATH_MAX];

    // 1. Resolve canonical base directory
    if (realpath(base_dir, base_canonical) == NULL) {
        return -1;
    }

    // 2. Safely construct raw path
    if (snprintf(raw_path, sizeof(raw_path), "%s/%s", base_canonical, user_filename) >= (int)sizeof(raw_path)) {
        return -1; // Path truncated
    }

    // 3. Resolve canonical target path
    if (realpath(raw_path, target_canonical) == NULL) {
        return -1; // File does not exist or access error
    }

    // 4. Verify canonical target begins with base_canonical + '/'
    size_t base_len = strlen(base_canonical);
    if (strncmp(target_canonical, base_canonical, base_len) != 0 || 
        target_canonical[base_len] != '/') {
        return -1; // Traversal detected!
    }

    return open(target_canonical, O_RDONLY | O_CLOEXEC);
}

Checkliste für Engineering & Systemsicherheit

References