Cosa fa#
Un attaccante manipola un parametro che il server usa per costruire un percorso file, inserendo sequenze ../ per uscire dalla directory base e leggere file arbitrari sul filesystem.
Path Traversal è navigazione non autorizzata nel filesystem
TL;DR#
# Il server carica un'immagine in questo modo:
GET /image?filename=37.jpg
# Il server risolve internamente:
/var/www/images/37.jpg
# L'attaccante sostituisce filename con un path traversal:
GET /image?filename=../../../etc/passwd
# Il server risolve:
/var/www/images/../../../etc/passwd → /etc/passwdVettori di attacco#
Il parametro vulnerabile non e' solo nel query string. Qualsiasi punto dove il server riceve un nome o percorso file puo' essere un vettore:
- Query parameter:
?filename=37.jpg - Body POST:
{"file": "report.pdf"}in una API JSON - Cookie:
document=invoice.pdf - Path segment:
/files/37.jpg - Header HTTP:
X-File-Name:,Referer:
Come costruire il payload#
Per uscire da una directory servono sequenze ../ proporzionali alla profondita' del path base.
- Regola dei troppi
../: Su Unix, aggiungere sequenze oltre la root (/) e' sicuro; il path rimane/. Partire con 8-10../garantisce di arrivare alla radice.
Tecniche di Bypass (Evasione Filtri)#
Gli sviluppatori spesso implementano filtri per bloccare le sequenze ../. Esistono diverse tecniche per aggirarli:
1. Filtri non ricorsivi#
Se il server rimuove ../ in modo non ricorsivo (una sola volta), si usa una sequenza nidificata:
- Payload:
....//o....\/ - Risultato dopo il filtro:
../
2. URL Encoding & Double Encoding#
Utile se il server decodifica l'input dopo aver applicato i filtri di sicurezza:
- Single Encoding:
%2e%2e%2f - Double Encoding:
%252e%252e%252f(dove%25e' l'crypto del simbolo%)
3. Validazione del Prefisso (Base Folder)#
Se il server richiede che il percorso inizi con una cartella specifica:
- Payload:
/var/www/images/../../../etc/passwd
4. Null Byte Injection (%00)#
Utilizzato su sistemi legacy (es: PHP < 5.3) per terminare la stringa del percorso prima di un'estensione forzata dal server:
- Payload:
../../../etc/passwd%00.jpg - Effetto: Il sistema operativo smette di leggere allo zero binario, ignorando
.jpg.
Target utili#
| File | Contenuto utile |
|---|---|
/etc/passwd | Utenti, servizi attivi, home directory |
/etc/hosts | Hostname interni, altri server nella rete |
/proc/self/environ | Variabili d'ambiente del processo (possibili API key) |
/var/log/apache2/access.log | Log di accesso — utile per log poisoning |
../../../var/www/html/config.php | Credenziali database |
Mitigazioni e Difesa: La Canonicalizzazione#
Il modo più efficace per prevenire il Path Traversal è evitare di passare input fornito dall'utente direttamente alle API del filesystem. Se non è possibile evitarlo, la Canonicalizzazione (Canonicalization) è la linea di difesa fondamentale.
Cos'è la Canonicalizzazione?#
È il processo di conversione di un percorso (path) che può contenere caratteri "rumorosi" (../, ./, link simbolici, crypto multipli) nella sua forma più semplice, univoca e assoluta.
Perché è fondamentale?#
Un server può essere ingannato da stringhe diverse che puntano allo stesso file:
../../etc/passwd/var/www/html/../../etc/passwd/etc/passwd
La Canonicalizzazione è come se il server eseguisse un cd virtuale fino alla destinazione finale per vedere dove atterra realmente, ignorando i trucchi sulla "busta".
L'Analogia del Recinto (Dev vs SOC)#
Immagina che la tua cartella sicura sia un recinto chiamato /var/www/html/. Tutto quello che c'è fuori è privato.
L'attaccante invia: /var/www/html/../../../etc/passwd.
Visione Developer (Senza Canonicalizzazione): Il codice controlla solo la stringa grezza: "Inizia con /var/www/html/?". Risposta: SÌ. Il controllo passa, ma il sistema operativo risolve i
../e legge il file fuori dal recinto.Visione SOC Analyst (Con Canonicalizzazione): Il codice prima "pulisce" il percorso risolvendo i
../. La stringa diventa:/etc/passwd. Il codice ora controlla la "vera faccia" dell'input: "Il percorso REALE (/etc/passwd) inizia con /var/www/html/?". Risposta: NO. L'attacco viene bloccato perché hai guardato dove puntava davvero, non cosa c'era scritto sulla busta.
Esempio in Pseudo-codice: Controllo Debole vs Robusto#
// --- VISIONE DEVELOPER (DEBOLE) ---
// Input: "/var/www/html/../../../etc/passwd"
if (userInput.startsWith("/var/www/html/")) {
// PASSATO: La stringa inizia effettivamente con il prefisso richiesto.
// DISASTRO: Il Sistema Operativo risolverà i ../ e leggerà /etc/passwd.
fs.readFile(userInput);
}
// --- VISIONE SOC ANALYST (ROBUSTO) ---
// Input: "/var/www/html/../../../etc/passwd"
const realPath = path.resolve(userInput); // Canonicalizzazione
// realPath ora è diventato "/etc/passwd"
if (realPath.startsWith("/var/www/html/")) {
// BLOCCATO: "/etc/passwd" NON inizia con "/var/www/html/".
fs.readFile(realPath);
}la Canonicalizzazione è il processo di trasformazione di un percorso relativo o "sporco" (es. ../../etc/passwd) nella sua forma assoluta e univoca (es. /etc/passwd).
Come funziona la difesa corretta#
- Input: L'utente invia
../../etc/passwd. - Canonicalizzazione: Il sistema operativo o il linguaggio (es.
realpath()in PHP,os.path.abspath()in Python) risolve tutti i puntatori e restituisce il percorso reale:/etc/passwd. - Verifica (Sanitizzazione): Il server ora confronta il percorso reale (
/etc/passwd) con la directory autorizzata (/var/www/images/). - Risultato: Poiché
/etc/passwdnon inizia con/var/www/images/, l'accesso viene negato.
Senza la canonicalizzazione, il server confronta "mele con pere": una stringa relativa (../../) contro un prefisso assoluto. La difesa fallisce sempre.
Esempi di Implementazione#
PHP:
<?php
$base_dir = '/var/www/images/';
$requested = $_GET['filename'];
// realpath risolve tutti i ../ e i link simbolici (Canonicalizzazione)
$full_path = realpath($base_dir . $requested);
// Verifica che il percorso risolto inizi con la directory base
if ($full_path !<mark> false && strpos($full_path, $base_dir) </mark>= 0) {
readfile($full_path);
} else {
header('HTTP/1.0 403 Forbidden');
die('Access denied.');
}
?>
Node.js:
const path = require('path');
const fs = require('fs');
const baseDir = path.join(__dirname, 'images');
const requestedFile = req.query.filename;
// path.resolve() risolve il percorso assoluto (Canonicalizzazione)
const fullPath = path.resolve(baseDir, requestedFile);
// Verifica che il path risolto sia ancora all'interno di baseDir
if (fullPath.startsWith(baseDir)) {
fs.readFile(fullPath, (err, data) => {
// ... processa file
});
} else {
res.status(403).send('Access denied');
}LFI e RFI - Local e Remote File Inclusion#
LFI (Local File Inclusion) e RFI (Remote File Inclusion) sono l'escalation del path traversal: invece di leggere il file, il server lo include ed esegue come parte del rendering della pagina. Il salto da "leggo il file" a "eseguo codice" cambia completamente la superficie di danno.
sequenceDiagram
participant ATK as Attaccante
participant SRV as Server PHP
Note over ATK, SRV: Fase 1 - Poison del log
ATK ->> SRV: GET /page HTTP/1.1\nUser-Agent:
SRV ->> SRV: Apache registra User-Agent nel log
Note over ATK, SRV: Fase 2 - LFI + esecuzione
ATK ->> SRV: GET /?page=../../../../var/log/apache2/access.log&cmd=id
SRV ->> SRV: include(log) → PHP interpreta il codice iniettato
SRV ->> ATK: uid=33(www-data) gid=33(www-data)
Note over ATK: RCE ottenuta. Prossimo step: reverse shell
Differenza critica tra LFI e RFI:
| Path Traversal | LFI | RFI | |
|---|---|---|---|
| Azione | Legge file | Include/esegue file locale | Include/esegue file remoto |
| Impatto | Information Disclosure | Info Disclosure + possibile RCE | Quasi sempre RCE |
| Requisito | Parametro path non sanificato | include() con input non sanificato | allow_url_include = On |
| Fix | Canonicalization + whitelist | Evitare include() con input utente | Disabilitare allow_url_include |
allow_url_include e' disabilitato di default in PHP moderno. Se trovato abilitato su un server, e' quasi certamente una misconfiguration legacy o una backdoor intenzionale. Il pentest puo' sfruttarla immediatamente per RCE.
Scenario Reale#
Un analista Blue Team riceve un alert IDS per richieste HTTP anomale. Nei log vede:
GET /api/download?file=../../../../etc/passwd HTTP/1.1
GET /?page=../../../../var/log/apache2/access.log&cmd=whoami HTTP/1.1La prima e' un path traversal classico. La seconda, combinata con un User-Agent anomalo nelle righe precedenti, indica un tentativo di LFI via log poisoning.
Risposta: blocco IP, analisi dei log per confermare se il server ha risposto con 200 OK (esfiltrazione riuscita). Controllare anche l'access log per User-Agent anomali nelle ore precedenti: il poisoning avviene sempre prima dell'inclusione.
Dev parallel: in Node.js usare sempre
path.resolve()seguito da una verifica che il path risultante sia dentro labaseDirautorizzata. Non concatenare mai__dirname + req.params.file: il path traversal e' immediato. Lo stesso principio vale perinclude()in PHP: mai passare input utente direttamente come argomento, usare una whitelist di template nomi validi.
Collegato a#
- how-web-works — contesto HTTP e parametri URL
- command-injection — RCE tramite altri vettori di input non sanificato
- xss — altro vettore di injection, stesso principio di input non validato
- sql-injection — famiglia delle injection vulnerabilities


