flawopen.com/path-traversal/Rust

● CWE-918 · Haute
Recherche · FlawOpen

Path Traversal in Rust

How PathBuf::join allows directory escapes in Rust, and how to enforce containment using std::fs::canonicalize and starts_with.

💡 Explication en Langage Simple (ELI5)

Imaginez un lecteur de carte d'hôtel programmé pour n'ouvrir que les chambres du 2e étage. Si un client tape '../../master-safe' sur le digicode, la serrure vulnérable remonte le couloir et déverrouille le coffre-fort principal du gérant.

Concepts Clés et Termes

Web Application Security
Composant d'architecture clé affecté par CWE-918.
CWE-918
Classification standard Common Weakness Enumeration (CWE) pour path-traversal-rust.
Defense-in-Depth
Vérification d'ingénierie multicouche et isolation des limites à l'exécution.

Déroulement de l'Attaque Étape par Étape

Step 1

Saisie de Chemin de Fichier par le Client

Un point de terminaison accepte un nom de fichier ou un identifiant de ressource via un paramètre HTTP.

Step 2

Injection de Séquence de Traversée de Répertoire

L'attaquant injecte des séquences relatives comme '../', '..%2f' ou des chemins absolus non autorisés.

Step 3

Contournement de la Limite du Système de Fichiers

Le backend concatène naïvement le fichier sans vérifier le chemin canonique ni restreindre le répertoire racine.

Step 4

Divulgation ou Écriture Arbitraire de Fichiers

Le runtime ouvre et renvoie des fichiers système critiques (/etc/passwd, secrets d'API) à l'attaquant.

Code Source : Vulnérable vs Sécurisé

✕ IMPLÉMENTATION VULNÉRABLE
// VULNERABLE: PathBuf::join allows directory traversal
use std::fs;
use std::path::PathBuf;

fn read_user_file(filename: &str) -> Result<Vec<u8>, std::io::Error> {
    let base_dir = PathBuf::from("/var/app/public/files");
    // Attacker input: "../../etc/passwd"
    let target = base_dir.join(filename);
    
    // Reads arbitrary system files!
    fs::read(target)
}
✓ PATCH SÉCURISÉ ET ROBUSTE
// HARDENED: Canonicalize path and assert directory prefix
use std::fs;
use std::path::{Path, PathBuf};
use std::io::{Error, ErrorKind};

fn read_user_file(filename: &str) -> Result<Vec<u8>, Error> {
    let base_dir = Path::new("/var/app/public/files").canonicalize()?;
    
    // 1. Join and canonicalize target path (resolves .. and symlinks)
    let target = base_dir.join(filename);
    let resolved = target.canonicalize()?;
    
    // 2. Strict boundary check: resolved must start with base_dir
    if !resolved.starts_with(&base_dir) {
        return Err(Error::new(ErrorKind::PermissionDenied, "Path traversal attempt detected"));
    }
    
    if !resolved.is_file() {
        return Err(Error::new(ErrorKind::NotFound, "File not found"));
    }
    
    fs::read(resolved)
}

Liste de Contrôle de Sécurité pour l'Ingénierie

References