
Vaultwarden 1.37.3 con X-Real-IP predefinito trusted-local aggira i limiti di frequenza di accesso su Docker NAT
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vaultwarden-ip-header-spoof
Vaultwarden 1.37.3 - dani-garcia
Il valore predefinito IP_HEADER=X-Real-IP insieme a IP_HEADER_TRUSTED_PROXIES=local considera qualsiasi peer TCP non globale come reverse proxy. Il NAT della porta pubblicata di Docker standard è RFC1918. Un X-Real-IP fornito dal client diventa la chiave del rate-limit. Valori univoci non condividono mai un burst.
| ID | nessuna CVE al momento |
| CWE | CWE-307 / CWE-290 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Prodotto | Vaultwarden |
| Affetto | 1.37.3 (eb212e23). Immagine vaultwarden/server:1.37.3. Porta pubblicata Docker / userland-proxy / NAT Desktop, o qualsiasi peer RFC1918. |
| Auth | non autenticato |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
L'attaccante rimuove la protezione brute-force basata sull'IP. Invia un nuovo X-Real-IP a ogni tentativo. Il governor è una DashMap con chiave su quell'indirizzo, quindi burst e periodo non scattano mai.
POST /identity/connect/token password-grant. Il burst predefinito è 10 per 60 secondi per IP. Header univoci azzerano il bucket./api/two-factor/send-email-login, POST /admin, più hint non autenticato / prelogin / delete-recover / auth-request / Send access usano tutti ClientIp come chiave.docker run -p 80:80 standard e compose 80:80 rendono il peer un indirizzo bridge o NAT Desktop. trusted=local è !is_global(peer). Corrisponde.Un reverse proxy che sostituisce X-Real-IP con il client reale, e un bind sull'host il cui peer è un indirizzo globale, mantengono entrambi il governor onesto. Il buco è il default più una porta pubblicata senza sovrascrittura.
Ho fissato dani-garcia/vaultwarden 1.37.3 (eb212e23) dopo le GHSA di 1.35.x / 1.36.0 / 1.37.0. GHSA-c5rv ha messo send-email-login su LIMITER_LOGIN. Il residuo è come viene scelto ClientIp.
config.rs imposta come default ip_header a X-Real-IP e ip_header_trusted_proxies a local. auth.rs ip_header_is_trusted tratta local come !is_global(remote). ratelimit.rs usa come chiave LIMITER_LOGIN su quell'IpAddr. Bridge Docker, NAT VM Desktop e loopback sono tutti non globali.
Ho avviato vaultwarden/server:1.37.3 standard su loopback 127.0.0.1:18170:80 senza overwrite-proxy, più una seconda istanza su 18171 con IP_HEADER=none. Registrato [email protected]. Password-grant con una password errata e nessun header ha restituito 429 alla richiesta 11 (burst 10). Lo stesso grant con X-Real-IP: 198.51.100.{n} univoco per tentativo è rimasto HTTP 400 quattordici volte, zero 429. Sull'istanza none, 203.0.113.{n} univoci hanno comunque restituito 429 a 11.
SUCCESS VAULTWARDEN-IP-HEADER-SPOOF nohdr-429-at=11 spoof-400=14 spoof-429=0 none-429-at=11 VAULTWARDEN-IP-HEADER-SPOOF-WITNESS
Vicoli ciechi già registrati: l'immagine 1.37.3 non era locale, quindi il primo compose ha atteso un pull; il primo /alive è stata una risposta vuota mentre Rocket si bindava, poi 200 al secondo tentativo; il JSON di registrazione fittizio (masterPasswordHash + una key corta) è stato sufficiente, nessun retry con chiave lunga. Una reverse shell. Teatro. L'oracolo è 429 senza l'header e 400 con uno univoco.
cd lab
./run.sh
Target solo http://127.0.0.1:18170 (default) e http://127.0.0.1:18171 (IP_HEADER=none). Immagine vaultwarden/server:1.37.3.
Impostare IP_HEADER come default a none, oppure impostare IP_HEADER_TRUSTED_PROXIES come default a una lista vuota / CIDR proxy esplicito. local come "qualsiasi peer RFC1918" è il caso della porta pubblicata. L'esistenza di un header di forwarding non è prova di un proxy.
src/config.rs (ip_header, ip_header_trusted_proxies)src/auth.rs (ip_header_is_trusted, ClientIp)src/ratelimit.rs