Skip to main content
  1. Concetti/

Path Traversal, LFI e RFI

·7 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents

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/passwd

Vettori 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 %25 e' 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
#

FileContenuto utile
/etc/passwdUtenti, servizi attivi, home directory
/etc/hostsHostname interni, altri server nella rete
/proc/self/environVariabili d'ambiente del processo (possibili API key)
/var/log/apache2/access.logLog di accesso — utile per log poisoning
../../../var/www/html/config.phpCredenziali 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
Intuizione Rapida

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: . 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);
}
In Sintesi

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
#

  1. Input: L'utente invia ../../etc/passwd.
  2. 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.
  3. Verifica (Sanitizzazione): Il server ora confronta il percorso reale (/etc/passwd) con la directory autorizzata (/var/www/images/).
  4. Risultato: Poiché /etc/passwd non inizia con /var/www/images/, l'accesso viene negato.
Important

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 TraversalLFIRFI
AzioneLegge fileInclude/esegue file localeInclude/esegue file remoto
ImpattoInformation DisclosureInfo Disclosure + possibile RCEQuasi sempre RCE
RequisitoParametro path non sanificatoinclude() con input non sanificatoallow_url_include = On
FixCanonicalization + whitelistEvitare include() con input utenteDisabilitare allow_url_include
Warning

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.1

La 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 la baseDir autorizzata. Non concatenare mai __dirname + req.params.file: il path traversal e' immediato. Lo stesso principio vale per include() 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

Related