Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
vaultwarden-ip-header-spoof — Vaultwarden 1.37.3 default X-Real-IP trusted-local bypasses login rate limits on Docker NAT | Kitploit
Tools/GitHubGitHub/abraxas/vaultwarden-ip-header-spoof
Defensive ToolsPassword AttacksVulnerability AnalysisExploitationWeb SecurityPenetration TestingAuthenticationPapers & Research

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
GitHub
abraxas/vaultwarden-ip-header-spoof

vaultwarden-ip-header-spoof

Vaultwarden 1.37.3 default X-Real-IP trusted-local bypasses login rate limits on Docker NAT

View Repository
613 days agoNot yet reviewed
Share

Abraxas Labs - vaultwarden-ip-header-spoof

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  vaultwarden-ip-header-spoof

vaultwarden-ip-header-spoof

Vaultwarden 1.37.3 - dani-garcia

Default IP_HEADER=X-Real-IP plus IP_HEADER_TRUSTED_PROXIES=local treats any non-global TCP peer as a reverse proxy. Stock Docker published-port NAT is RFC1918. A client-supplied X-Real-IP becomes the rate-limit key. Unique values never share a burst.

IDno CVE yet
CWECWE-307 / CWE-290
CVSSHigh: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
ProductVaultwarden
Affected1.37.3 (eb212e23). Image vaultwarden/server:1.37.3. Docker published-port / userland-proxy / Desktop NAT, or any RFC1918 peer.
Authunauthenticated
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

The attacker removes IP-keyed brute-force protection. They send a fresh X-Real-IP on every try. The governor is a DashMap keyed on that address, so burst and period never trip.

  • Guess master passwords without a 429. POST /identity/connect/token password-grant. Default burst is 10 per 60 seconds per IP. Unique headers reset the bucket.
  • Burn email-2FA and admin-token governors the same way. /api/two-factor/send-email-login, POST /admin, plus unauth hint / prelogin / delete-recover / auth-request / Send access all key on ClientIp.
  • Reach the container through the published port. Stock docker run -p 80:80 and compose 80:80 make the peer a bridge or Desktop NAT address. trusted=local is !is_global(peer). That matches.
  • 2FA after a correct master password still runs. This is unlimited guesses, not a second-factor skip.

A reverse proxy that replaces X-Real-IP with the real client, and a host bind whose peer is a global address, both keep the governor honest. The hole is the default plus a published port with no overwrite.

How I found it

I pinned dani-garcia/vaultwarden 1.37.3 (eb212e23) after the 1.35.x / 1.36.0 / 1.37.0 GHSAs. GHSA-c5rv put send-email-login on LIMITER_LOGIN. The leftover is how ClientIp is chosen.

config.rs defaults ip_header to X-Real-IP and ip_header_trusted_proxies to local. auth.rs ip_header_is_trusted treats local as !is_global(remote). ratelimit.rs keys LIMITER_LOGIN on that IpAddr. Docker bridge, Desktop VM NAT, and loopback are all non-global.

I stood up stock vaultwarden/server:1.37.3 on loopback 127.0.0.1:18170:80 with no overwrite-proxy, plus a second instance on 18171 with IP_HEADER=none. Registered [email protected]. Password-grant with a wrong password and no header 429'd at request 11 (burst 10). The same grant with X-Real-IP: 198.51.100.{n} unique per try stayed HTTP 400 fourteen times, zero 429s. On the none instance, unique 203.0.113.{n} still 429'd at 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

Wrong turns already recorded: the 1.37.3 image was not local, so the first compose waited on a pull; first /alive was an empty reply while Rocket bound, then 200 on wait 2; dummy register JSON (masterPasswordHash + a short key) was enough, no long-key retry. A reverse shell. Theatre. The oracle is 429 without the header and 400 with a unique one.

Lab

cd lab
./run.sh

Target only http://127.0.0.1:18170 (default) and http://127.0.0.1:18171 (IP_HEADER=none). Image vaultwarden/server:1.37.3.

The fix

Default IP_HEADER to none, or default IP_HEADER_TRUSTED_PROXIES to an empty list / explicit proxy CIDR. local as "any RFC1918 peer" is the published-port case. Existence of a forwarding header is not proof of a proxy.

References

  • github.com/dani-garcia/vaultwarden tag 1.37.3
  • src/config.rs (ip_header, ip_header_trusted_proxies)
  • src/auth.rs (ip_header_is_trusted, ClientIp)
  • src/ratelimit.rs
  • CWE-307

License

GNU Affero GPL v3.0

Download Tool