Security Advisories

unauthenticated remote admin via reverse-proxy + localhost-trust

CVECVE-2026 SeverityCRITICAL ProductGPUStack VendorGPUStack Published2026-06-14
CVSSCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Metadata

Target: gpustack/gpustack @ commit 6bee5883 (HEAD on 2026-05-14)

Field Value
Severity Critical
CVSS v3.1 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS Score 9.8
CWE CWE-290 (Authentication Bypass by Spoofing), CWE-284 (Improper Access Control)

Summary

When force_auth_localhost is False (default) and gateway_mode resolves to disabled (default on bare metal), gpustack.api.auth.get_current_user auto-promotes any HTTP request whose TCP peer IP is 127.0.0.1/::1 to the first admin user.

In any deployment that puts a reverse proxy in front of gpustack on the same host — the canonical self-hosted shape (nginx, Caddy, Traefik, Docker with same-host upstream, Kubernetes sidecar) every TCP connection arriving at gpustack comes from the proxy at 127.0.0.1. The bypass therefore triggers for every unauthenticated remote request, returning full admin and allowing an attacker to mint a persistent admin API key (scope=["*"]) usable from any source IP thereafter.

Vulnerable code

gpustack/api/auth.py (auto-admin branch, ~lines 122–125 of HEAD 6bee5883):

if user is None and client_ip_getter(request=request) == "127.0.0.1":
    if not server_config.force_auth_localhost:
        user = await User.first_by_field(session, "is_admin", True)

client_ip_getter (~lines 80–89) uses Starlette's request.client.host (the TCP peer IP). It only consults the X-GPUStack-Real-IP header when gateway_mode != disabled, which is not the default.

Default config (gpustack/config/config.py):

References

Notes

  1. GPUStack fixing without giving any credits until it published.
Zoomed image