Alle Artikel

The first disclosure from our Mythos harness

Chainguard Team

Our harness's first public finding is in vLLM: one client could force the output of another client's request. The bug had been sitting in vLLM's public issue tracker for four months, filed as an ordinary bug.

The harness

Last month, Chainguard Co-founder and CEO Dan Lorenc wrote about Chainguard joining Anthropic's Project Glasswing. Part of that work has been building a harness around Mythos so we can look for vulnerabilities in open source ourselves, in addition to handling the ones Athena members send us.

Each target gets built from source in an isolated microVM with no credentials, and the agents test the running software instead of only reading the code. They work roughly the way a human researcher would, poking at something, following it down, and dropping it when it goes nowhere. We run a lot of them in parallel, each assigned to a different part of the attack surface.

After finding a flaw, the agents keep going. A bug on its own usually isn't that interesting. What matters is whether an attacker can reach it and what they get when they do, so the agents keep working until they've traced a realistic path in or concluded there isn't one.

Every finding has to be reproduced on a clean build before anyone on our team sees it, and then an engineer checks each claim against the source. Maintainers are already dealing with a flood of AI-generated reports that fall apart on inspection, and we don't want to add to that.

A finished report includes the root cause down to specific lines, the conditions an attack requires, a proposed fix with a test, and a mitigation operators can use until they upgrade. It also lists what we didn't test. If we can only prove a narrower claim, we report the narrower claim.

Where we're pointing our harness

We're focused on the open source that production systems depend on: projects that sit in front of traffic, handle sensitive data, or run inside a lot of other companies' infrastructure. AI serving engines like vLLM are near the top of the list. 

So far, we've run the harness against over 65 projects and validated over 680 findings. A number of those are in coordinated disclosure now.

When a finding holds up, we report it privately to the maintainers, help with the fix where we can, and wait for it to ship. Then we publish an advisory with the maintainers and ship fixed builds to Chainguard customers. We don't publish exploit code.

The vLLM bug

vLLM is an open source engine that a lot of teams use to serve large language models. In versions before v0.31.0, one client could force the output of other clients on the same engine. In our test, a victim asked for the capital of France and got back "violin violin violin violin violin violin." It came back as a normal, successful response, and nothing was logged.

That's the obvious case, since anyone reading it would know something was off. But plenty of LLM output is never read by a person. It feeds moderation checks, classifiers, and numbers that other systems act on. If an attacker limits those answers to "yes," they pass on someone else's request, and nothing is recorded.

How the bug is exploited

vLLM requests can include allowed_token_ids, a list of the only tokens the model may generate. Developers use it to force a yes/no answer or keep output inside a fixed vocabulary. Internally, vLLM stores these lists in a shared table with one row per running request.

When a request finishes, its row is reassigned to the next request. The code is supposed to clear the old restriction before handing the row over. What it actually does is move the restriction, leave the old copy in place, and write a new one only if the incoming request supplied its own. Most requests don't. Because the table stores restrictions as "block everything except these tokens," the new request ends up with the previous request's list applied to it.

To exploit it, an attacker holds open a long-running request that allows exactly one token. Whichever request lands on the stale row next can only produce that token. We reproduced this three out of three times. The attacker can't pick the victim, and can't see the victim's prompt or response. vLLM rated the issue Low, which we think is fair for the bug in isolation. The fix is a one-line change that clears the row, plus a related fix.

What the run looked like

We let the harness run on vLLM for just under 11 hours (10h 48m), and it went through about 115 million tokens. In that time, the agents poked at 30 different parts of vLLM. They closed out 17. Five of those they worked all the way through, and the other 12 they dropped once it was clear nothing was there. They also chased 8 findings that went nowhere.

Anything that looked real had to reproduce on a clean host before one of us looked at it. That happened 37 times, and 13 held up. The allowed_token_ids bug was one of them, and it reproduced three out of three times.

It was already public

We weren't the first to find this. @glaziermag reported it in vLLM's public tracker on May 28 as #43894, and someone proposed a fix the following day. Both were labeled as a regular bug, and they stayed that way for four months.

That isn't a criticism of anyone involved - a report about wrong output under batching looks like a correctness bug, and nearly always that's all it is. Recognizing a vulnerability means asking what an attacker could do with a bug, and most bug reports never get looked at that way. We run into this a lot.

Our harness independently found the same issue. On September 23, we told the vLLM maintainers it had a security impact, and the fix was merged a few days later and shipped in v0.31.0 on October 2. The advisory, GHSA-6cxc-2vcg-w5qc, went out on October 6, crediting @glaziermag and @jperezdealgaba.

Thanks to the vLLM maintainers for moving quickly once the security angle was clear. They published an advisory less than two weeks after our report.

Who's affected

Your exposure is highest if clients you don't control send requests to a shared engine, such as customers of a hosted API or tenants on a multi-tenant platform. Any one of them can use this against the others. If your server was started with --api-key, note that the key is only checked on routes under /v1, /v2, /inference and /cohere. The SageMaker-compatible /invocations route serves the same completion requests without a key, so clients without a key can reach this flaw too. This is documented behavior: vLLM's security guide says not to rely on --api-key alone and recommends a reverse proxy that only allows the endpoints you intend to expose.

If only your own services talk to the engine, exploiting this would take an insider or a compromised service. If the engine serves a single application, the cross-client issues don't apply. You should upgrade either way.

The same release fixes three related flaws in vLLM's shared state, found by the researcher @KernelClint (one of them through Patch the Planet, from Trail of Bits and OpenAI):

Finding

Advisory

Severity

Fixed in

One client's output restriction applies to another client's request

GHSA-6cxc-2vcg-w5qc

Low (CVE requested)

v0.31.0

A cache salt and a LoRA adapter name collide, so clients share a cache block

GHSA-rh6f-3x46-j33q

Moderate (CVE requested)

v0.31.0

Predictable image-cache keys let a client with no account seed or probe the cache

GHSA-rh6f-3x46-j33q

Moderate (CVE requested)

v0.31.0 (key collision)

The scale-out transport trusts caller data, so a forged object can crash the engine for every tenant

GHSA-ph72-cqr5-qpp7

Moderate

v0.30.0

What to do

Upgrade to vLLM v0.31.0 or later. That covers the output flaw and the three related issues above.

Give untrusted clients their own engine. If different customers, teams, or users share one engine, split them up. vLLM doesn't isolate tenants within a single process, and its documentation now says so explicitly.

Put a gateway in front of vLLM. Have it authenticate every request and forward only the routes your application uses. If you can't upgrade yet and don't need allowed_token_ids, have the gateway reject or strip that parameter from untrusted clients. That blocks the output flaw without changing vLLM.

Check your completions. vLLM's server logs won't show this. If you keep completion records, look for responses that ignore the prompt and repeat a single token, or stay within a very small set of tokens, especially during heavy load.

We're not the only ones

None of this is new. People have been reading code, fuzzing it, and reporting bugs for decades, and open source is much safer for it. Most of what we built into the harness came from that work. What's different now is volume. Frontier models make this kind of search far cheaper, so a lot more of it is happening, from other Project Glasswing members, from Patch the Planet, and from independent researchers, some of whom use these models as well. @KernelClint alone found three of the four vLLM flaws covered here.

On balance, that's good for open source. More bugs are getting found, and most of the people finding them report them. But not everyone will. And some of what does get reported won't be recognized as a security issue. It'll get fixed as a normal bug, the way this one almost was, with no advisory or CVE, so nobody downstream knows they were exposed. Reports are also arriving faster than the existing process was designed for, and we don't expect that to slow down.

Why running the latest version matters

This is the first Chainguard harness finding to go public. More are in coordinated disclosure now, and plenty of other researchers have their own.

Taken together, that points to something that applies well beyond vLLM. Most people scanning open source look at the latest version, since that's what maintainers can fix. So that's where fixes land in every project: in the newest release, often quietly and often without a backport.

Old versions don't get safer with age; people just stop looking at them. And a security fix that isn't described as one, like this one, is only visible to someone reading the diff. Attackers can point models at diffs, too. So the advice applies to everything you run. All this scanning only protects you if you're on the version that got fixed, which means you can quickly move any dependency to its latest release and treat that as routine work rather than a special project.

Thank you to the individuals and groups that made this disclosure possible:

If you maintain a project and want to hear from us before a disclosure affects it, email cna@chainguard.dev.

Share this article

Start building with Chainguard