Skip to content
, ,

GPUStack CVE-2026-102895: CISA KEV Flaw, How to Check, Upgrade and Harden

9–13 minutes
Shocked woman in a server room, PATCH card, hook cvss 9.8 patch gpustack now

CISA has added CVE-2026-102895, a critical login bypass in the GPUStack AI model server, to its Known Exploited Vulnerabilities (KEV) catalog. Attackers are already using it to get admin access and run reverse shells.

If you run GPUStack to serve models on your own GPUs, check your version today. This guide explains the flaw, how to check if you are exposed, how to upgrade, and how to harden your setup.

TLDR

  • The flaw: GPUStack before 2.2.0 treated “localhost” requests as the admin. Attackers can fake that with a proxy or one HTTP header.
  • Severity: CVSS 9.8, no login needed, exploited in the wild against a v2.1.1 server.
  • KEV: added on November 9, 2026, with a November 12 due date for US federal agencies.
  • Fix: 2.2.0 fixes this bug, but upgrade to 2.2.2 or later to also fix a 9.1 SAML bypass. The newest release is 2.3.1.
  • After upgrading: hunt for unknown admin keys, users and custom inference backends.

What GPUStack Is and Why This Matters

GPUStack is an open-source tool for running AI models on your own GPU clusters. One server manages many worker machines, and it serves models through backends like vLLM and SGLang. It installs through pip or Docker Hub.

That central server is the problem. It holds cloud provider keys, worker tokens and the power to launch code on every GPU node it controls. So one bypass on the server can reach your whole cluster.

The CISA timeline is short:

  • July 31, 2026: GPUStack published GitHub advisory GHSA-gj9m-f8ph-pmhr for the bug.
  • November 4, 2026: the CVE record for CVE-2026-102895 was published.
  • November 9, 2026: CISA added it to the KEV catalog with three other flaws.
  • November 12, 2026: the KEV due date for US federal civilian agencies.

Warning: a KEV listing means CISA has evidence of real attacks. Even if you are not a US agency, treat that three-day deadline as a strong hint to patch now.

How CVE-2026-102895 Works

The bug lives in the login code, in a function called get_current_user inside gpustack/api/auth.py. When a request came from a loopback address like 127.0.0.1, GPUStack treated it as the built-in administrator, with no password.

That only happened when a setting called force_auth_localhost was false. False was the default.

Two easy ways to look like localhost

The advisory lists two ways a remote attacker could appear to be 127.0.0.1:

  • Same-host reverse proxy: if Nginx, Caddy or similar ran on the same machine and connected to GPUStack over 127.0.0.1, every outside request looked local.
  • Forged header: a client could just send X-GPUStack-Real-IP: 127.0.0.1 and be treated as local.

The advisory puts it plainly: either path gives “full, unauthenticated administrative access over the network.” The root cause is trusting where a request comes from as proof of who sent it.

What an attacker can do with admin access

  • Take over accounts: manage users, models, workers and settings.
  • Stay in: create persistent admin API keys.
  • Steal secrets: read worker and cloud provider credentials.
  • Run code: use POST /v2/inference-backends/from-yaml to define a custom backend that runs commands on worker hosts.

That last point is what happened in the wild. Researcher Javier Sixto of ulex.fr reported attacks on a v2.1.1 server, where attackers created custom backends that ran reverse shells.

Detail CVE-2026-102895
Product GPUStack (pip package gpustack)
CVSS 3.1 9.8 Critical (network, low complexity, no privileges, no user action)
Weakness CWE-287, CWE-290, CWE-346
Affected All versions before 2.2.0
Fixed in 2.2.0
Exploited Yes, against a v2.1.1 deployment
KEV added / due November 9 / November 12, 2026

The Second Bug: CVE-2026-102894

Coverage of the KEV entry often mentions a second flaw. CVE-2026-102894 (CVSS 9.1) is in the /auth/saml/callback route.

That route accepted a SAML login message and signed the named user in without checking its signature, sender or time limits. The route is on by default, so the developers say it works even if you never set up SAML SSO.

  • Affected: versions 0.7.1 up to, but not including, 2.2.2.
  • Fixed in: 2.2.2.
  • Exploited: no confirmed attacks so far. UC Berkeley researchers shared a private exploit with the developers.

The 2.2.2 release also fixed missing authorization on several cluster endpoints in 2.2.0 and 2.2.1. That bug allowed cross-user access and cluster takeover. So stopping at 2.2.0 is not enough.

Which Versions Are Safe

Here is how each recent release stacks up. Release dates come from the GPUStack page on PyPI.

Version Released CVE-2026-102895 CVE-2026-102894
2.1.x and older Up to Apr 21, 2026 Vulnerable Vulnerable (from 0.7.1)
2.2.0 Jun 18, 2026 Fixed Vulnerable
2.2.1 Jul 3, 2026 Fixed Vulnerable
2.2.2 Jul 24, 2026 Fixed Fixed
2.2.3 Jul 31, 2026 Fixed Fixed
2.3.0 Oct 23, 2026 Fixed Fixed
2.3.1 (latest) Nov 11, 2026 Fixed Fixed
Quick answer: 2.2.2 is the minimum safe version for both CVEs. If you are upgrading anyway, go straight to the latest release, which is 2.3.1 at the time of writing.

How to Check Your GPUStack Version

Use the method that matches how you installed it.

If you installed with pip

Run this on the server, inside the same Python environment GPUStack uses:

pip show gpustack

Look at the Version: line. Anything below 2.2.2 needs an upgrade.

If you run the Docker image

Check which image tag your container uses:

sudo docker ps --filter name=gpustack --format "{{.Names}}  {{.Image}}"

If the image shows only gpustack/gpustack with no tag, it is latest as of the day you pulled it. That could be months old. You can also look at the version shown in the GPUStack web UI.

Tip: check every worker too. Workers run the same image, and the docs say to upgrade the server first, then the workers.

How to Upgrade GPUStack

These steps follow the official GPUStack upgrade page. Take a backup of your data volume first, since the database holds your users, keys and model settings.

  1. Back up your data. For Docker, the default volume is gpustack-data, mounted at /var/lib/gpustack. Copy it, or snapshot the disk, before you change anything.
  2. Pick a target version. Use 2.2.2 at a minimum. The latest at the time of writing is 2.3.1.
  3. Upgrade the server (Docker). Pull the new image with docker pull gpustack/gpustack:vX.Y.Z, replacing X.Y.Z with your target. Then stop and remove the old container and start a new one with the same volume and ports.
  4. Upgrade the server (pip). Run pip install --upgrade gpustack for the latest, or pip install gpustack==2.2.2 for a specific release. Python must be 3.11 or 3.12.
  5. Restart the service. Restart GPUStack the way you normally run it, such as systemd or Docker.
  6. Upgrade each worker. Repeat the image pull or pip upgrade on every worker node, after the server is done.
  7. Check the logs. Run sudo docker logs -f gpustack and confirm the server starts cleanly and workers reconnect.
  8. Confirm the version. Run the version check again and make sure it shows 2.2.2 or later.

For a Docker server, the recreate step looks like this, based on the official run command:

sudo docker stop gpustack
sudo docker rm gpustack
sudo docker run -d --name gpustack \
    --restart unless-stopped \
    -p 80:80 \
    --volume gpustack-data:/var/lib/gpustack \
    gpustack/gpustack:vX.Y.Z

Note: if you jump to 2.2.2 or later, two names changed. The Helm worker Service is now gpustack-worker instead of worker, and GPUSTACK_USAGE_ROLLUP_TIMEZONE became GPUSTACK_TIMEZONE. Update your scripts and charts to match.

If You Cannot Upgrade Today

The advisory and the release notes list stopgaps. Use them only until you can upgrade.

  • Force local logins: start the server with --force-auth-localhost so loopback requests must log in. On 2.2.0 and later this flag is a no-op, because the bad rule is gone.
  • Strip the header: make your reverse proxy remove any X-GPUStack-Real-IP header sent by clients.
  • Fix the proxy path: make sure the proxy cannot make outside traffic look like it comes from 127.0.0.1.
  • Block SAML: block outside access to /auth/saml/callback at the firewall, especially if you do not use SAML.
  • Get off the internet: keep the API behind a VPN or an edge login, never open to the public.

Check for Signs of Compromise

Patching closes the door. It does not remove anyone who already got in. If your server was ever reachable from the internet on a vulnerable version, check these after you upgrade:

  • API keys: look for admin keys you did not create, and delete them.
  • Users: look for new or changed accounts, especially admins.
  • Inference backends: look for custom backends you did not add. The attackers in the wild used them to run reverse shells.
  • Proxy logs: search for requests that carried X-GPUStack-Real-IP, and for calls to /v2/inference-backends/from-yaml.
  • Worker hosts: look for odd processes or outbound connections on GPU nodes.

If you find anything, assume your stored secrets are gone. Rotate cloud provider keys, worker tokens and any model hub tokens that GPUStack held.

Harden Your GPUStack Server

Once you are patched, a few habits make the next bug much less painful:

  1. Never expose the admin UI publicly. Put it behind a VPN, a private network or an identity-aware proxy.
  2. Change the default admin password. The first password lives in /var/lib/gpustack/initial_admin_password. Replace it right after install.
  3. Pin image tags. Use a version tag, not bare latest, so you always know what you run.
  4. Watch advisories. Follow the GPUStack GitHub security tab and the KEV catalog.
  5. Limit secrets. Give GPUStack cloud keys with the smallest set of permissions it needs.

The same ideas apply to any self-hosted AI tool. My MCP security best practices guide covers the habits that carry over. If you run models on a single machine instead, see the best Ollama models or running DeepSeek locally.

My Take

This bug is a classic. “Trust localhost” feels safe on a laptop, but it breaks the moment a reverse proxy sits on the same box. Most production setups have exactly that.

The timing is also worth noting. The GitHub advisory and the 2.2.0 fix came months before the CVE and the KEV listing. Teams that update often were already safe, and teams that pin old versions were the ones at risk.

GPU servers are juicy targets because they hold expensive compute and cloud keys. If you are building your own AI infrastructure, as in my GPU networks explainer, treat the control server like a crown jewel.

Troubleshooting: Common Problems and Fixes

  • pip says no matching version: check your Python version. GPUStack needs Python 3.11 or 3.12.
  • Workers do not reconnect after the upgrade: upgrade the workers to the same version as the server, then restart them.
  • Helm upgrade breaks worker networking: the worker Service was renamed to gpustack-worker in 2.2.2. Update any references to the old name.
  • Usage reports show the wrong time zone: rename GPUSTACK_USAGE_ROLLUP_TIMEZONE to GPUSTACK_TIMEZONE.
  • Docker still runs the old version: you pulled the image but kept the old container. Stop it, remove it and start a new one with the same volume.
  • Lost admin access after removing localhost trust: scripts that relied on local admin calls now need a real API key. Create one in the UI and update the scripts.

Frequently Asked Questions

What is CVE-2026-102895?

It is a 9.8 critical login bypass in GPUStack before 2.2.0. Requests that looked like they came from localhost got admin rights with no password.

Is CVE-2026-102895 being exploited?

Yes. Attacks were seen on a v2.1.1 server, and CISA added the flaw to its KEV catalog on November 9, 2026.

Which GPUStack version should I install?

Use 2.2.2 or later, which fixes both this flaw and the SAML bypass. The latest release at the time of writing is 2.3.1.

Am I safe if GPUStack is not on the internet?

Your risk is much lower, but not zero. Anyone on the same network can send the forged header, so you should still upgrade.

Does the KEV due date apply to me?

The November 12 deadline legally applies to US federal civilian agencies. Everyone else should treat it as a strong signal to patch fast.

Does upgrading remove an attacker who already got in?

No. Upgrading closes the bug, but you must still delete unknown API keys, users and backends, and rotate stored secrets.

What does force_auth_localhost do now?

On old versions it made localhost callers log in. Since the fix, the docs list the flag as deprecated and a no-op, because localhost callers are always authenticated.

Sources

Free weekly email

Get new AI guides every week

One short weekly email with our newest local AI, ComfyUI and AI tool guides. No spam, no pop-ups, just the good stuff.

Free. Unsubscribe anytime in one click. We only use your email to send this newsletter. See our privacy policy.

Shocked woman in a server room, PATCH card, hook cvss 9.8 patch gpustack now