P.K. SHARMA

Cyber security intelligence, AI governance, practitioner analysis

GPUStack advisory says loopback trust gives unauthenticated admin; exploited, fixed in 2.2.0, on CISA KEV

GPUStack's own advisory says a request that looks like loopback becomes the built in administrator, and that this was exploited against a 2.1.1 deployment; 2.2.0 fixes it and CISA listed it on 9 November. The fix commit sat on the main branch 174 days before that listing (derived).

By · LinkedIn · · 17 min read

The fix was on the main branch 174 days before CISA listed the flaw (derived)

GPUStack, which the CVE record calls "an open-source GPU cluster manager for AI model serving and GPU instance provisioning", has a commit on its main branch titled "fix: remove localhost auto-admin bypass". The commit page dates it 19 May 2026 at 10:30 UTC. CISA added CVE-2026-102895 to its Known Exploited Vulnerabilities catalogue on 9 November 2026, with a due date of 12 November. That is 174 days after the commit (derived). Release 2.2.0, the version the GitHub advisory names as the fix, appeared on 18 June 2026, 30 days after the commit (derived), and 144 days before the KEV listing (derived).

What this does not establish. The pages do not say when exploitation began, who did it, or how many servers were reached. The advisory says the flaw "has been observed exploited in the wild against a v2.1.1 deployment", with "attacker-created custom backends running reverse shells". That is one deployment, no date, no actor. The commit also does not prove the 2.2.0 fix is complete: the advisory says the fix hardened header handling, and the one commit the CVE record links does not touch that code (see below). Nothing here says how many people run an affected version, or that every version before 2.2.0 is affected, because the advisory prints the range as "< 2.2.0" with no lower bound.

The advisory describes two ways for a remote request to look like it came from the server itself. The one that needs no header at all is a reverse proxy running on the same host as GPUStack. That matters because one workaround the advisory lists, stripping the named header at the proxy, does not close that path on its own. This briefing read the advisory, the CVE record, the CISA catalogue file, the 2.2.0 release page, the pull request page and the commit page from 06:45 UTC on Wednesday 11 November 2026. A KEV listing and a record can change within the hour, so every statement about them carries that time stamp.

One selling interest to state once: GPUStack is open source per its CVE record, and the advisory sits in the project's own repository, so the project is describing the weakness in, and the repair of, its own code. Several outside parties are credited in the advisory text, among them researchers at Johns Hopkins University, and a further party is credited with reporting the exploitation. This briefing names no individual.

What the advisory says happens: two ways to look like 127.0.0.1

In GPUStack before 2.2.0 the function get_current_user, in gpustack/api/auth.py, "promotes any request whose source is considered loopback to the built-in administrator account" when the setting force_auth_localhost is False, which was the default. The source address comes from a function called client_ip_getter. The advisory says that function can be made to report 127.0.0.1 without credentials in two ways.

Path A, the same host reverse proxy. The advisory describes what it calls the canonical self hosted shape, with GPUStack behind a reverse proxy on the same host that forwards to a loopback address. In that layout every remote request reaches GPUStack with a TCP peer of 127.0.0.1, so the loopback branch fires. The advisory's words: "No header or credential is required." It says it verified this end to end against a repository HEAD it identifies by a short hash, not by a release number.

Path B, a client supplied header. The advisory says a forged X-GPUStack-Real-IP header carrying a loopback value can cause the request source to be treated as loopback. The CVE record words it as a "client-supplied X-GPUStack-Real-IP loopback value". The advisory states the root cause as "Trusting the network source of a request (socket peer, or a client-controllable header) as an authentication signal." and adds that once a same host proxy is in front, the peer is always loopback, so the assumption that localhost is trusted no longer holds.

Drawn by this site from the GitHub advisory GHSA-gj9m-f8ph-pmhr and the CVE record, read 11 November 2026. Card colours and the dashed fix arrow are this site's; slate cards are not stated or are reach statements from the advisory.

What an unauthenticated caller gets. The advisory lists: enumerate and modify users, models, workers and configuration; mint persistent admin API keys with a wildcard scope, valid from any source IP afterwards; read worker credentials and cloud or provider secrets; and, with the admin only route POST /v2/inference-backends/from-yaml in play, code execution on worker hosts. CISA's catalogue entry repeats the same list in its short description. The advisory does not say what the from-yaml route accepts or runs, and this briefing adds nothing beyond that: it prints no request and no payload.

Severity. The CVE record carries a CVSS 3.1 base score of 9.8 in the container of the CNA, GitHub_M, which is also the assigner. The advisory page shows the same score and vector, and labels it Critical. The CISA ADP container in the record adds an SSVC entry timestamped 5 November 2026: Exploitation active, Automatable yes, Technical Impact total. Neither document says who calculated the base score beyond that container; this briefing did not query the National Vulnerability Database.

The clock: a quiet fix, a late record, a KEV entry

The dates come from five different pages. The table sets each beside its source and keeps them in the order they happened. The one that does not sit comfortably is the advisory's: its page prints 31 July 2026 as the published date, which is 60 days before the CVE record's reserved date of 29 September 2026 (derived). The page does not say why, and it shows no edit history, so whether the exploitation sentence was in the 31 July text is not stated.

Dates as each page prints them, read 11 November 2026. Intervals in the last column are derived by this site.

EventDate as printed, and whereDerived interval
Fix commit on main, titled remove localhost auto-admin bypass19 May 2026, committed 10:30:10 UTC (commit page data)Start
Release 2.2.018 Jun 2026, 15:54 UTC (release page)30 days after the commit
Advisory GHSA-gj9m-f8ph-pmhrPublished Jul 31, 2026 (advisory page)43 days after release 2.2.0
CVE ID reserved2026-09-29 (CVE record)60 days after the advisory date
CVE record published2026-11-04 (CVE record)96 days after the advisory date
CISA ADP SSVC entry2026-11-05 (CVE record)1 day after the record was published
Added to CISA KEV2026-11-09 (KEV file, release 2026.11.10)174 days after the commit; 101 after the advisory date
KEV due date2026-11-12 (KEV file)3 days after the listing
Drawn by this site from the commit, release and advisory pages, the CVE record and the CISA KEV file, all read 11 November 2026. Intervals are derived. Grey cards are not stated.

The release notes for 2.2.0 list new models, GPU instance management, Helm chart installation, database support and many other changes. Searching the page text for the pull request number, the words loopback, localhost, advisory and authentication bypass finds none of them (derived). A reader who relies on the release notes to decide whether to upgrade would not learn from that page that the release removes an unauthenticated admin path. The advisory is the place that says so.

CISA's catalogue entry says forensic triage is Yes and known ransomware use is Unknown, and its required action cites BOD 26-04 and the Forensics Triage Requirements. Its notes line says the vulnerability "affects an open-source component, third-party library, protocol, or proprietary implementation that could be used by different products". That wording is attributed here to CISA's catalogue notes. It appears in 18 of the 1,779 entries in release 2026.11.10, so it is a recurring note and not text written about GPUStack (derived). It names no product that embeds GPUStack, and no page this briefing read does.

What the fix changed, from the commit the CVE record links

The advisory says 2.2.0 fixed the flaw: "the loopback auto-admin path was removed and client-IP / forwarding-header handling was hardened". Its remediation line says to "only accept a real-client-IP header from an authenticated/trusted gateway (never from arbitrary clients)". The CVE record links the pull request, a commit and the release. The commit page shows five files changed, with 67 additions and 17 deletions: the documentation page for the start command, the authentication module, the start command, the configuration module and a test file.

In the authentication module the commit deletes three lines (shown under Technical details). Together they are the loopback branch: if no user was found and the client IP equals 127.0.0.1, and the flag is not true, use the first admin user. The setting force_auth_localhost is not removed: it becomes a deprecated option that does nothing, kept so existing configuration files still load, and the server logs a warning if it is set. The commit's test file adds cases that must raise an unauthorised error with no credentials, including a request with a TCP peer of 127.0.0.1 that carries a forwarded for header, a request from IPv6 loopback, and one from an outside address.

What the commit page does not show. The commit does not touch client_ip_getter and contains no reference to the X-GPUStack-Real-IP header (derived by searching the commit page). The advisory says header handling was hardened in 2.2.0. Either that change is in a different commit, or the advisory's wording is wider than this commit. The pull request has three commits, titled in order "fix: remove localhost auto-admin bypass", "fix: require worker auth on /config endpoint" and "feat: mint local JWT in reset-admin-password fallback". This briefing read the first commit's page and the pull request page, not the other two commits, so it cannot say whether the header change is in them. The pull request description reads "No description provided.", and its title calls the change a refactor.

What the commit does not do (derived from its diff). The three changed lines in the authentication module are the loopback branch, and the other four files hold documentation, option handling and tests. The diff contains no code that revokes or lists API keys, so nothing in it removes administrator keys created while the flaw was open, and the advisory says such keys are valid from any source IP. Upgrading removes the path in; whether it removes what an intruder left behind is a different question, and neither page answers it.

What the sources state, and what they do not

What the GitHub advisory, the CVE record and the CISA KEV file state and do not state, read 11 November 2026.

QuestionStatedNot stated
Exploited?Advisory and record: observed in the wild against a 2.1.1 deployment, custom backends running reverse shells. KEV listing. SSVC: Exploitation activeActor, first date, number of victims, whether the same group is behind every case, any indicator
Ransomware?KEV: known ransomware campaign use UnknownAny ransomware link either way
Which versions?Affected: < 2.2.0. Patched: 2.2.0A lower bound; whether any older line is unaffected; whether any release between 2.1.1 and 2.2.0 differs
Default install reachable?The default of force_auth_localhost is False. A same host reverse proxy gives path A with no headerWhether a default install with no proxy and a remote bind is reachable, and how common the proxy layout is. Which proxies other than a same host proxy to a loopback address
What the header doesA forged X-GPUStack-Real-IP loopback value can make the source look localWhether the header was honoured from any peer or only some, and what the 2.2.0 hardening changed in that code
Is the fix complete?Advisory: auto admin path removed, header handling hardened. Commit: auto admin branch deletedWhich commit hardened header handling; any test of the header path; whether other loopback trust exists
Cleanup after exposure?Admin API keys can be minted and last; secrets can be readWhether the fix revokes keys, how to list keys created in the open window, a detection method, a log line to hunt
Other products?KEV notes: the component could be used by different productsAny named product that embeds GPUStack
Exposure?NothingAny count of reachable servers

Labels that stand in for a control

Localhost. The word suggests the server's own machine, a place only the operator can reach. A reverse proxy on that machine makes every outside request local. The advisory calls this the canonical self hosted shape, so the safer looking layout, a proxy in front of the application, is the layout the flaw fires in. A name that promises locality is not a check on who is asking.

force_auth_localhost. The name reads as a safeguard. Per the advisory it was False by default, and it was the workaround, not the norm. After the fix it is a deprecated option that does nothing, so a configuration file that sets it true no longer changes anything. A reader who has the line in a file should read it as history, not as a control.

Strip the header at the edge. The advisory lists this workaround. It handles path B only. The advisory's own description of path A says no header or credential is required, so a proxy rule that removes X-GPUStack-Real-IP leaves a same host proxy forwarding to a loopback address exactly as before. The advisory pairs it with the first workaround, force_auth_localhost set true, and with keeping the API off untrusted networks.

Added to KEV. The listing is evidence of exploitation. It is not a description of it. The catalogue file carries no actor, count or date of first use, and its forensic triage field is CISA's flag, not a finding about any given server.

Where the pages differ

Differences between the pages read on 11 November 2026, left unresolved.

PointOne page saysAnother says
Advisory dateAdvisory page: published Jul 31, 2026CVE record: ID reserved 2026-09-29, published 2026-11-04. The page does not explain a date before the reservation
Who reported whatAdvisory text: independent reports from several outside parties, and a separate credit for reporting exploitationAdvisory Credits panel: two entries, both labelled Finder, and none for the exploitation report
What the fix did to headersAdvisory: client IP and forwarding header handling was hardenedThe linked commit page: no change to client_ip_getter and no mention of X-GPUStack-Real-IP
How the change is namedCommit: a fix. Advisory: a fix for a critical flawPull request title: a refactor, with no description. Release notes: no mention of it
What the KEV note impliesKEV: the component could be used by different productsNo page lists one. The note is recurring catalogue wording (18 of 1,779 entries)

No secondary source is cited. Several news sites will cover a KEV listing; this briefing took every figure from the primary pages and read no news coverage.

What to do, in order

Take this with you

The advisory's own fix and workarounds

  • Upgrade to GPUStack 2.2.0 or later.
  • Workaround: set force_auth_localhost=true to require authentication even for loopback requests.
  • Workaround: make sure the reverse proxy or gateway cannot cause GPUStack to treat external traffic as loopback, and strip any client supplied X-GPUStack-Real-IP header at the edge.
  • Workaround: keep the API off untrusted networks, with a VPN or edge authentication.

Take this with you

This site's suggestions (not the advisory's advice)

  • Find every GPUStack server and record its version, including copies run for trials, in a lab, or inside another team's cluster. The KEV note says the component could be used by different products, so ask vendors whether any of their products include it.
  • Upgrade the server to 2.2.0 or later. If that must wait, use the first workaround, not only the header rule, because the header rule does not cover a same host proxy.
  • Find out whether the server port can be reached from outside the cluster network, directly or through a proxy that forwards to a loopback address. Treat a reachable server on a version before 2.2.0 as possibly accessed. CISA marks forensic triage Yes for this entry.
  • Before and after the upgrade, list users and API keys. Revoke keys and accounts you cannot match to a person or a service, because the fix as read does not remove keys minted earlier.
  • Rotate the worker credentials and the cloud and provider secrets the server holds, if the server was reachable on a version before 2.2.0.
  • Review inference backends, custom backends in particular, and the worker hosts that ran them. The advisory says custom backends running reverse shells were the form the exploitation took; it prints no further indicator, so the review is of what you defined, not a list to match.
  • Look at proxy and access logs for requests to the from-yaml route and to the admin API from outside the cluster network. The advisory does not name a log line, so this is this site's suggestion, not a detection rule.

Technical details

Everything below is copied from the advisory, the CVE record, the CISA catalogue file or the fix commit page. None of the pages prints an exploit request, a payload, an address, a hash or any other indicator of compromise, and this briefing adds none. The advisory prints the forged header in prose with a loopback value; this briefing quotes the header name and the workaround, not a ready made request.

Identifiers, weakness types, score and versions. A defender uses these to match scanner output and asset inventory, and to file the record.

Text
CVE-2026-102895
GHSA-gj9m-f8ph-pmhr
CWE-287: Improper Authentication
CWE-290: Authentication Bypass by Spoofing
CWE-346: Origin Validation Error
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (base score 9.8, CRITICAL, in the CNA container)
Affected versions: < 2.2.0
Patched versions: 2.2.0
GitHub advisory GHSA-gj9m-f8ph-pmhr, CVE record and CISA KEV file, read 11 November 2026, record and advisory.

The proxy shape the advisory calls the exposed layout. It is the advisory's description of a same host reverse proxy forwarding to a loopback address. A defender searches proxy configuration for this shape on any host that also runs GPUStack, as a way to find servers where path A applies. It is not a test.

Text
proxy_pass http://127.0.0.1:<port>
GitHub advisory, Impact section, read 11 November 2026.

The route the advisory ties to code execution. It is admin only, and the unauthenticated caller reaches it through this flaw. A defender searches proxy and access logs for requests to it from outside the cluster network, and reviews the backends it created.

Text
POST /v2/inference-backends/from-yaml
GitHub advisory GHSA-gj9m-f8ph-pmhr, CVE record and CISA KEV file, read 11 November 2026, Impact section and catalogue short description.

The advisory's workarounds, word for word. The first sets a configuration option, the second is a proxy rule, the third is network placement. After the upgrade the first option is ignored, per the commit.

Text
Set force_auth_localhost=true to require authentication even for loopback requests.
Ensure the reverse proxy / gateway cannot cause GPUStack to treat external traffic as loopback, and strip any client-supplied X-GPUStack-Real-IP header at the edge.
Keep the API off untrusted networks (VPN / edge authentication).
GitHub advisory, Workarounds section, read 11 November 2026.

The exploitation statement. One sentence in the advisory, repeated in the CVE record with different wording. It is the only description of the activity any page prints.

Text
This vulnerability has been observed exploited in the wild against a v2.1.1 deployment (attacker-created custom backends running reverse shells).
GitHub advisory, Impact section, read 11 November 2026.

The code the fix commit deleted. These three lines are the loopback branch in the authentication module, with the diff markers as the commit page shows them. A reviewer uses them to confirm that a build after the commit no longer contains the branch.

Text
@@ -120,9 +120,6 @@ async def get_current_user(
-        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)
GitHub commit page for the fix, authentication module diff, read 11 November 2026.

The warning the commit adds when the old option is set. After the fix, a configuration that still sets force_auth_localhost logs this message, which is a way to find servers whose files still carry it. The text is Python source as the diff shows it.

Text
+    if cfg.force_auth_localhost is not None:
+        logger.warning(
+            "force_auth_localhost is deprecated and ignored: localhost callers "
+            "are always authenticated. This flag will be removed in a future "
+            "release."
+        )
GitHub commit page for the fix, start command diff, read 11 November 2026.

CISA's catalogue fields and the SSVC entry. The catalogue fields carry the due date; the SSVC entry is CISA's coordinator view in the CVE record. Neither names an actor or a count.

Text
vulnerabilityName: GPUStack Improper Authentication Vulnerability
dateAdded: 2026-11-09
dueDate: 2026-11-12
knownRansomwareCampaignUse: Unknown
forensicTriage: Yes
cwes: CWE-287, CWE-290, CWE-346
catalogVersion: 2026.11.10
CISA Known Exploited Vulnerabilities catalogue, release 2026.11.10, entry for CVE-2026-102895, read as saved by the lead on 11 November 2026.
Text
Exploitation: active
Automatable: yes
Technical Impact: total
(role CISA Coordinator, timestamp 2026-11-05T00:00:00+00:00, version 2.0.3)
CVE record, CISA ADP container, read 11 November 2026.

The question the pages leave open

The fix was public on the main branch 174 days before CISA listed the flaw, the release notes never mention it, and the fix as read does not remove administrator API keys that were minted while the path was open. If a GPUStack server was reachable through a same host proxy on any version before 2.2.0, what in your logs could show that no one used it?

Key facts

Sources

  1. PrimaryGitHub security advisory GHSA-gj9m-f8ph-pmhr on the gpustack/gpustack repository, page dated Jul 31, 2026: impact, root cause, patches, workarounds, credits, severity. Read in full on 11 November 2026. GPUStack is open source and the advisory is the project's ownGPUStack (GitHub)accessed 2026-11-11
  2. PrimaryCVE record CVE-2026-102895, CNA GitHub_M, published 2026-11-04, updated 2026-11-10: description, CVSS 3.1, CWEs, affected range, and the CISA ADP SSVC and KEV entries. Fetched and compared with the lead's saved copy: identicalCVE Program (MITRE CVE Services)accessed 2026-11-11
  3. PrimaryKnown Exploited Vulnerabilities catalogue, release 2026.11.10 (1,779 entries), entry for CVE-2026-102895: dates, forensic triage, notes, required action. Read as saved by the lead on 11 November 2026CISAaccessed 2026-11-11
  4. PrimaryRelease page for v2.2.0, released 18 Jun 2026 15:54 UTC: read in full; the notes do not mention the flawGPUStack (GitHub)accessed 2026-11-11
  5. PrimaryPull request 5346, refactor: remove localhost auto-admin bypass, merged 19 May 2026: three commit titles, no descriptionGPUStack (GitHub)accessed 2026-11-11
  6. PrimaryCommit 53f42a6, fix: remove localhost auto-admin bypass, 19 May 2026: five files changed; the deleted loopback branch and the deprecated optionGPUStack (GitHub)accessed 2026-11-11

Share this briefing

Know someone who owns this problem? Send it to them.

Related briefings

The briefing, in your inbox

Practitioner analysis of cyber and AI security news. No vendor noise.

How often

Every new briefing in one email, at 7am, or at 7am, 12:30pm and 6pm. Nothing is sent when nothing is new. Unsubscribe any time.