Severity Daily

IT and AI security incidents, checked against the primary source

Tag: Bifrost

  • Bifrost’s LLM gateway loaded attacker-supplied native code without authentication, and the fix shipped as a changelog line about path normalization

    Bifrost’s LLM gateway loaded attacker-supplied native code without authentication, and the fix shipped as a changelog line about path normalization

    JFrog’s CVE for the Maxim AI gateway landed on September 6, 2026, eleven days after the release that fixed it — a release whose notes describe the fix as a path normalization flaw.

    What happened

    CVE-2026-86242 was published to NVD on September 6, 2026, with a published value of 2026-09-06T12:17:15.583 and an identical lastModified. The CNA is JFrog, under the source identifier [email protected]. The vulnStatus is Received, so NVD has not analyzed the record and it carries no CPE. It carries no cisaExploitAdd, so there is no KEV listing.

    The product is Bifrost, the open-source LLM gateway published by Maxim AI. A gateway of this kind sits between an organization’s applications and the model providers behind them, holding the provider API keys and seeing every prompt and completion in flight.

    The record’s description is unusually specific for a CVE, and the specificity is the story. It states that the HTTP transport “accepts an enabled custom plugin whose path is an HTTP URL through unauthenticated POST /api/plugins when management authentication is disabled (the default, governance.auth_config.is_enabled=false).” From there: “The shared-object loader treats an http-prefixed path as a download URL, writes the body to a temporary .so, and passes it to Go’s plugin.Open. After a successful open, optional Init runs immediately with the supplied config as the Bifrost process user.”

    Then it splits the impact by how the binary was built. “On documented dynamically linked builds (DYNAMIC=1 / no static-link flags), which the vendor requires for custom Go plugins, plugin.Open is expected to succeed and this is unauthenticated remote code execution. On the published statically linked Docker image, plugin.Open fails with Dynamic loading not supported, so that build class is only server-side request forgery.” The record also explains its own attack-complexity rating: “Attack complexity is High because the attacker cannot force RCE on the default static image and a loadable plugin must match the host Go version, OS, architecture, and linkage.”

    JFrog scored it CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H, base 8.1, High. The weaknesses are CWE-94, CWE-284, and CWE-306. The affected range is everything before 2.0.0, with 2.0.0-prerelease3 and later marked unaffected. The record adds a separate sentence about the older line: “The 1.6.x HTTP transport line through 1.6.11 does not contain the fix.”

    The fix itself is public and has been for weeks. Pull request 5763 in the maximhq/bifrost repository is titled “path normalization auth bypass,” and the pull request page shows it merged on August 3, 2026. Its description states the mechanism plainly: “Loading a custom plugin path causes native code (a .so) to be dlopen()‘d directly into the gateway process. Previously, this was allowed even when dashboard authentication was disabled or unconfigured — meaning any caller who could reach the management API could inject arbitrary native code.”

    The commit the CVE cites, e0057ff, touches roughly thirty files. In handlers/plugins.go, the create and update handlers now require authentication before any database write when a non-builtin plugin path is supplied. In handlers/middlewares.go, a BifrostContextKeyAuthBypassed context key is set when dashboard authentication is disabled or unconfigured, so handlers can tell a bypassed session from an authenticated one. In core/network/ssrf.go and framework/plugins/utils.go, the plugin downloader moves to a net/http client using an SSRF-safe dialer that rejects non-HTTP schemes and blocks private and loopback addresses.

    That fix reached stable users in the HTTP transport’s 2.0.0 release. The GitHub API reports transports/v2.0.0 published at 2026-08-26T19:47:19Z. In the release notes, the entire public description of this work is one line in a list of bug fixes: “Fixed a path normalization flaw that allowed auth to be bypassed (#5763).” Across two retrievals of the release page, the strings “CVE-2026-86242,” “plugin.Open,” and “/api/plugins” do not appear anywhere on it.

    The GitHub security advisory the CVE record cites as its most detailed reference, GHSA-2qp8-4xgm-fw6g, returns a 404 to an unauthenticated fetch at both the repository advisory path and the GitHub Advisory Database path.

    Why it matters

    The most consequential sentence in the record is the parenthetical: management authentication is disabled by default. Everything else follows from it. A defect that requires an administrator to have turned off a protection is a configuration story. A defect that ships in the off position is a population story, and the population is whoever ran the gateway without going looking for governance.auth_config.is_enabled.

    What is reachable through that endpoint is not a data path but a code path. Go’s plugin.Open loads a shared object into the running process; there is no sandbox between a loaded plugin and the process that loaded it. For an LLM gateway the process in question holds every provider credential the deployment uses and observes every prompt and response in flight. Code execution there is not a stepping stone toward the interesting material. It is the interesting material.

    The build-class split deserves more attention than an 8.1 will get it, and it runs in an uncomfortable direction. The default published Docker image is statically linked, so plugin.Open fails and the same request degrades to server-side request forgery — still a real problem when the gateway can reach cloud metadata endpoints, but not code execution. The dynamically linked build is the one that executes attacker code, and per the record, that build is what the vendor requires for custom Go plugins. The deployments most likely to be on the vulnerable build class are therefore the ones using the plugin system on purpose. Severity here is decided by a compile flag, not by patch level, and no scanner reads compile flags.

    JFrog deserves credit for writing all of this into the record rather than leaving readers to find it. A CVE description that explains its own attack-complexity rating, and names the exact failure string on the safe build class, is better source material than most vendor advisories.

    The changelog line is the part that will cost somebody. An operator who upgraded from 1.6.x to 2.0.0 in late August read “fixed a path normalization flaw that allowed auth to be bypassed” and got the fix by accident, which is fine. An operator who read the same line, saw a breaking-change warning and a migration guide attached to a major version, and decided to stay on 1.6.x until a quieter month made a risk decision on information that did not describe the risk. Nothing in that sentence says unauthenticated native code loading. The pull request said it, in the words quoted above, five weeks ago; the release notes did not carry those words forward.

    And the remediation is expensive in a way that matters to that decision. There is no fix on the 1.6 line — the record says so explicitly, through 1.6.11. The only supported path off this is the 2.0.0 major version, which the release notes describe as carrying breaking changes, a migration guide, and a non-reversible database migration consolidating OAuth token tables. That is a real upgrade, not a point release, and it is now the minimum action for a flaw whose disclosed impact is unauthenticated code execution.

    What to do

    Upgrade the Bifrost HTTP transport to 2.0.0 or later. The record marks 2.0.0-prerelease3 and later as unaffected, and stable 2.0.0 published on August 26, 2026. There is no fixed release on the 1.6.x line through 1.6.11, so staying on 1.6 is not a patched state. Read the v2.0.0 migration guide first; the release carries breaking changes and a non-reversible OAuth token table migration.

    If the upgrade cannot happen immediately, the record’s own framing points at the interim control: the unauthenticated path exists “when management authentication is disabled (the default).” Enable management authentication by setting governance.auth_config.is_enabled to true, and confirm it is actually in effect rather than assumed.

    Independently of both, do not expose the management API to any network that does not need it, and block POST /api/plugins at the ingress except from an administrative source range. Because the pre-fix downloader fetches an arbitrary URL, egress filtering from the gateway host is worth having, particularly against cloud instance metadata addresses.

    Check which build you run. If the deployment uses the published static Docker image, exposure on the pre-fix code is server-side request forgery. If it was built with DYNAMIC=1 or without static-link flags — which is what running custom Go plugins requires — treat it as unauthenticated remote code execution and review the process for unexpected loaded objects and the plugin configuration for entries nobody added.

    Sourcing note

    Checked: the NVD record for CVE-2026-86242 through the NVD API; pull request 5763 and commit e0057ff355f831c251eabe9d0e44f3a3748532c6 in the maximhq/bifrost repository; the transports/v2.0.0 release page, retrieved twice, and the same release through the GitHub REST API for its publication timestamp. All quotations of the vulnerability description and of the pull request are taken from those primary artifacts.

    Could not reach: GHSA-2qp8-4xgm-fw6g, the GitHub security advisory the CVE record names as a reference, which returned 404 at both github.com/maximhq/bifrost/security/advisories/ and github.com/advisories/. Whatever additional detail that advisory carries is not reflected here. The GitHub REST API returned 403 for the pull request endpoint, so the August 3, 2026 merge date is taken from the pull request’s web page rather than from the API.

    Unresolved: this check found no JFrog research writeup alongside the CVE, so there is no proof-of-concept or telemetry to weigh. Neither Maxim AI nor JFrog has said anything about exploitation, and nothing here should be read as evidence of any. How many Bifrost deployments run dynamically linked builds is not knowable from public sources, which is the largest gap in judging how much of the affected population sits in the code-execution class rather than the request-forgery class.