Severity Daily

IT and AI security incidents, checked against the primary source

Tag: fail open

  • LiteLLM’s MCP endpoint answered a failed key check with an empty auth object, and CISA put it on a September 16 clock

    LiteLLM’s MCP endpoint answered a failed key check with an empty auth object, and CISA put it on a September 16 clock

    When LiteLLM’s key validation failed on its MCP endpoint, the code substituted an empty authorization object and let the request through — CISA added it to the KEV catalog on September 2 with a September 16 deadline.

    What happened

    CISA added CVE-2026-59822 in LiteLLM to the Known Exploited Vulnerabilities catalog on September 2, 2026. NVD’s record carries the fields verbatim: cisaExploitAdd of 2026-09-02, cisaActionDue of 2026-09-16, and the catalog name “BerriAI LiteLLM Improper Authentication Vulnerability.”

    LiteLLM is an AI gateway — a proxy that presents many model providers behind one OpenAI-compatible API, handles keys and budgets, and, increasingly, brokers Model Context Protocol tool calls on behalf of the applications in front of it. The flaw is in that last part. NVD’s description: “Prior to 1.84.0, LiteLLM’s MCP Streamable HTTP endpoint allowed an unauthenticated attacker to use a fabricated Authorization header to trigger an OAuth2 passthrough fallback path that replaced failed LiteLLM key validation with an empty UserAPIKeyAuth() object, allowing requests to reach MCP tooling without a valid LiteLLM key.”

    The project’s advisory, GHSA-7488-6r32-c95q, published June 30, 2026 and titled “MCP Authentication Bypass via OAuth2 Passthrough Fallback,” describes what that reaches: an attacker could “list and call configured MCP tools and access connected services exposed through MCP” with no valid credential. Affected versions are everything below 1.84.0. The fix is 1.84.0. The advisory credits a reporter it names as yaaras, and gives a workaround for anyone who cannot upgrade: disable the MCP routes, or block /mcp/ and related MCP resources at the reverse proxy or API gateway.

    Two scores sit on the record and neither is a dispute. GitHub’s advisory database supplies CVSS 4.0 at 8.8 High; [email protected] supplies CVSS 3.1 at 8.2 High on CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N. The CWE assignments are CWE-287 for improper authentication and CWE-306 for missing authentication for a critical function.

    The exploitation evidence traces to one place. NVD’s reference list includes Wiz’s writeup of an AI infrastructure honeypot, and that writeup states that Wiz “observed exploitation of this vulnerability in our honeypots, with requests using single-character tokens to probe model enumeration endpoints.” The captured request is a single line: GET /v1/models HTTP/1.1 with Authorization: Bearer x.

    Why it matters

    Look closely at what the code did, because the shape of it is the story. This is not a missing authentication check. LiteLLM validated the key. The validation failed. And on that failure path, the OAuth2 passthrough fallback substituted an empty UserAPIKeyAuth() object — a successfully-constructed authorization result carrying no identity — and execution continued as though a caller had been identified.

    This publication covered the identical shape yesterday in F5’s njs, where js_access control failed open when the JavaScript raised an exception. The lesson repeats because the mistake is structural: the error path of an authorization control is itself an authorization decision, and it is the path nobody writes a test for. A missing check gets caught in review, because the absence is visible. A check whose failure branch returns an empty success object looks, in a diff, like careful defensive coding.

    The word “fallback” is doing the damage here. A fallback is what you write when the primary path might legitimately not apply — and OAuth2 passthrough is exactly such a case, since the point of it is that some upstream MCP servers do their own authorization. But “this request might be authorized by someone else” and “this request failed my authorization” are different conditions, and the code treated them as one.

    Then there is what sits behind the endpoint. The instinct on reading “AI gateway breach” is to think about model access — someone burning your inference budget, or reading prompts. The MCP surface is a different thing. MCP tools are the gateway’s connections to real systems: databases, ticketing, source control, internal HTTP services, filesystems. Listing them tells an attacker what an organization has wired into its agents. Calling them is action taken against those systems, with whatever credentials the gateway holds for them, and the calls arrive from the gateway’s own network position and appear in downstream logs as the gateway. There is no user identity in any of it, because the authorization object was empty.

    That is also why the CVSS 3.1 vector deserves a second look. It reads C:H/I:L/A:N — high confidentiality impact, low integrity impact, no availability impact. For an endpoint whose defining capability is calling tools that change state in other systems, low integrity is a defensible reading of the gateway itself and an understatement of the deployment. It is the same component-versus-deployment gap that showed up on the Starlette entry CISA added the same day.

    The exploitation evidence needs to be stated at its real size, which is small. It is one vendor’s honeypot data, gathered over 90 days across AI and ML services. Wiz published no count of attempts, no dates within that window, no geography, and no figure for how many honeypots were hit. What it published is a pattern, and the pattern is informative even without a number: Authorization: Bearer x is a one-character token. Nobody sends that hoping it is the right key. It is a probe that asks whether any token gets through — the precise test for a fail-open bug, run indiscriminately. That is scanning behavior, and scanning behavior is what CISA’s exploitation determinations are usually built on.

    One more thing for anyone running LiteLLM. It is on the list of downstream projects affected by BadHost, the Starlette Host header flaw that CISA added to the catalog the same day, on the same September 16 deadline. A LiteLLM operator therefore has two KEV entries due at once: one in the application and one in the web framework underneath it, both of them authentication bypasses, both reached over the same listening port.

    The fix shipped June 30, 2026. The catalog listing came September 2 — 64 days. As with Kestra earlier in the same batch, the vendor did its part on time and the exposure being put on a clock is patch lag on self-hosted infrastructure that most organizations stood up quickly and have not yet folded into a normal patch cycle.

    What to do

    Get the running LiteLLM version. Anything below 1.84.0 is affected; 1.84.0 or later is fixed. Container deployments pinned to latest at build time are not evidence of anything — check the image that is actually running.

    If an upgrade cannot happen inside the window, apply the project’s own workaround: disable MCP routes, or block /mcp/ and the related MCP resources at the reverse proxy. That closes the path without touching the gateway’s model-serving function.

    Assume the endpoint was probed. Search access logs for requests to /mcp/ and to /v1/models carrying implausible bearer tokens — single characters, obvious test strings, repeated identical values from many addresses. Then check the tool side: MCP tool invocations that do not correlate to a known LiteLLM key or user, and activity in the connected systems arriving from the gateway’s address outside normal application hours. Because the bypass produced an empty authorization object rather than a borrowed one, an unauthenticated call will have no key attribution at all, which makes it findable if your logging records the key.

    If you find calls you cannot account for, rotate the credentials the MCP servers hold for downstream systems, not just the LiteLLM keys. The LiteLLM key was never the thing that was used.

    Federal agencies have until September 16, 2026. The required action carries CISA’s forensic triage clause and the discontinue-use provision where mitigations are unavailable, so the asset assessment is part of the obligation rather than a follow-up.

    Sourcing note

    KEV dates, the catalog vulnerability name, the required action text, the description, both CVSS entries with their assigners, and the CWE assignments come from NVD’s API record for CVE-2026-59822, which republishes CISA’s fields verbatim. The advisory title, affected and fixed versions, the description of what MCP access reaches, the workaround, and the reporter credit come from LiteLLM’s own GitHub Security Advisory GHSA-7488-6r32-c95q, published June 30, 2026.

    The exploitation observation is single-vendor: Wiz’s AI infrastructure honeypot writeup, referenced from the CVE record. It reports a 90-day observation window and gives the probe pattern, but publishes no count of attempts, no dates within the window, no geographic breakdown, and no number of honeypots involved. We asked for those figures and they are not in the source. Nothing here should be read as a measure of how widely the flaw has been exploited.

    CISA’s alert page and KEV catalog feed both return HTTP 403 to automated requests, so the catalog was reached through NVD — NIST republishing CISA, a government primary source, but one that lags the catalog and is not the catalog itself. Unresolved: whether the exploitation CISA relied on is the same activity Wiz observed or separate reporting, and whether any successful MCP tool calls, as opposed to enumeration probes, have been documented against a live deployment.

  • F5 njs access control fails open on an exception; nginx-saml parses attacker XML before checking the signature

    F5 njs access control fails open on an exception; nginx-saml parses attacker XML before checking the signature

    F5 published seven CVE records on September 2, and the sharpest is an access-control module that lets the request through when its own code throws an exception.

    What happened

    Seven vulnerability records naming F5 as the assigning authority reached the National Vulnerability Database on Wednesday, September 2, 2026, timestamped 4:17 p.m. UTC. Six of them are new. The seventh, CVE-2026-63020, a 3.1-rated spoofed-error-message issue in the BIG-IP configuration utility, points back to an older knowledge-base article and appears to be a backfill rather than part of this release.

    Three of the six are in NGINX JavaScript, the scripting module usually written as njs, and all three are fixed in njs 1.0.1.

    The one worth reading first is CVE-2026-18329, scored 8.2 under CVSS v3.1 and 8.8 under CVSS v4.0 by F5’s own product security team, with the vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N — network reachable, no privileges, no user interaction. The record describes a js_access handler doing asynchronous request-body processing, where “an exception is thrown during asynchronous access-control evaluation before an explicit access denial is returned.” The consequence is stated plainly: the access phase can fail open. The njs 1.0.1 changelog is blunter, calling it an “Access control bypass in js_access.” Affected njs versions are 0.9.9 and 1.0.0.

    CVE-2026-78689 is a heap out-of-bounds write in njs’s XML module, reachable through xml.exclusiveC14n() when it parses a crafted namespace prefix list. What makes it more than a parser bug is where F5 says that parser sits: the record singles out the nginx-saml reference implementation, which processes untrusted InclusiveNamespaces/@PrefixList values before it validates the signature. Affected njs versions run from 0.7.10 up to 1.0.1.

    CVE-2026-78222, 7.5 and 8.7, crashes an NGINX worker. F5’s text: “A vulnerability exists in NGINX JavaScript where a malformed HTTP response received by ngx.fetch() can crash an NGINX worker when trusted JavaScript reads Response.statusText.” The njs changelog names the specific trigger — an upstream “status line with an empty reason phrase.” It is classified CWE-476, a null pointer dereference, and it reaches back to njs 0.5.1, the widest affected range in the batch.

    Two more are configuration-generator injection flaws, both CWE-76. CVE-2026-77180, 8.3 and 8.7, is in NGINX Ingress Controller: “Multiple user-controllable fields are written into the generated NGINX configuration without sanitization.” An attacker who can write Ingress annotations through the Kubernetes API can inject directives, touch files, or take the service down. Affected: 5.0.0 through 5.6.0, and the long-term-support line 2026-lts-r1 through 2026-lts-r5. F5 credits kodareef5. CVE-2026-66362, 8.1 and 8.6, is the same shape in NGINX Gateway Fabric running NGINX Plus as its data plane, where values “from the Authentication Filter Custom Resource Definition clientID or cookieName fields, or in the clientSecret field of a Secret referenced by an Authentication Filter, are rendered directly into NGINX configuration templates without sanitization or escaping.” Fixed in 2.6.8.

    The sixth is not NGINX at all. CVE-2026-66842, 8.8 and 8.7, is a BIG-IP privilege escalation: “BIG-IP has a vulnerability where an authenticated user of any role may be able to create administrative user accounts” through an undisclosed request to the Traffic Management User Interface. F5 lists no workaround. It is credited to Dan Stefan Alexandru of Pentest-Tools and also affects BIG-IQ’s TMOS module.

    F5 reports no exploitation for any of the seven, and none carries a CISA KEV entry.

    Why it matters

    A fail-open access control is a different category of defect from a bug in software that happens to be exposed. js_access exists for one purpose: to decide whether a request is allowed. When the thing that decides has a failure mode of “allow,” every deployment that leaned on it has been quietly weaker than its configuration said, and nothing in a log would necessarily show it. The attacker’s job is not to find a bypass but to make the handler throw, and the exception does not have to come from the security logic itself.

    The remediation guidance is worth reading closely for the same reason. Alongside the upgrade, F5’s workaround for CVE-2026-18329 tells operators to “wrap your logic in a robust try/catch block with a default strict deny policy that returns an HTML 403 Forbidden response code.” It is also an instruction to write the deny-by-default behavior yourself, in a module whose entire job was to provide it. Anyone who deployed js_access without thinking through what happens on an unhandled rejection was relying on a guarantee the module did not make.

    CVE-2026-78689 carries a second lesson about ordering. Parsing attacker-controlled XML before checking the signature that is supposed to establish trust is a recurring failure across SAML implementations, and it turns a memory-safety bug in a namespace parser into a pre-authentication one. The reference implementation being the named at-risk consumer matters, because reference implementations get copied.

    That CVE also shows something about how severity is being reported right now. F5 scored it 8.1 High under CVSS v3.1 with AC:H, and 9.2 Critical under CVSS v4.0 with AC:L/AT:P. Same flaw, same assigner, same record, two different bands. This is not carelessness — v4.0 moved the “there is a precondition” idea out of attack complexity into a separate Attack Requirements metric, so the condition that pushed the v3.1 score down is represented differently rather than dropped. But a team that filters on “Critical” sees this flaw and a team that filters on the v3.1 base score does not, and both are reading the same record. F5’s v3.1 vector also asserts C:H/I:H/A:H, full compromise, while the record’s own text puts code execution at possible and unconfirmed: the score grades the worst case, the prose does not.

    The two injection flaws land in a familiar place: the boundary between “can edit Kubernetes objects” and “can execute in the proxy.” Ingress annotations and custom-resource fields feel like configuration, not code, and they are handed to people well short of cluster administrator. Where a template renders those strings unescaped, the RBAC grant that looked like a routing permission is a proxy-configuration permission.

    There is an awkward wrinkle in the recommended compensating control. F5 suggests admission policy — naming Kyverno, OPA Gatekeeper, and ValidatingAdmissionPolicy — to reject resources containing special characters. That is the right instinct. It is also worth knowing that Kyverno had a policy-exception bypass record, CVE-2026-84200, published to NVD the day before this batch, which this site covered on September 1. Admission policy is still worth deploying. But a compensating control is only as good as its own patch level, and an organization reading only F5’s advisory would not learn that.

    What to do

    • njs: upgrade to 1.0.1. This closes CVE-2026-18329, CVE-2026-78689, and CVE-2026-78222. The ranges differ — 0.9.9 and 1.0.0 for the access bypass, 0.7.10 up for the XML write, 0.5.1 up for the worker crash — so an old njs is exposed to the last two even if it predates the first.
    • If you run js_access: do not wait on the upgrade to add the try/catch and explicit 403 deny default F5 describes. Audit any handler doing asynchronous body work for paths that can throw.
    • If you run nginx-saml or anything derived from it: treat this as pre-authentication reachable and prioritize accordingly.
    • NGINX Ingress Controller: upgrade to 5.6.0, or 2026-lts-r5 on the LTS line. Until then, tighten RBAC so that write access to Ingress annotations is limited to trusted administrators, and add an admission policy that rejects special characters in annotation values.
    • NGINX Gateway Fabric: upgrade to 2.6.8. Review who can create or edit Authentication Filter resources and the Secrets they reference.
    • BIG-IP: upgrade to 21.1.0.1, 21.0.0.3, 17.5.1.8, or 17.1.3.4 depending on branch; BIG-IQ to 8.4.2.1. There is no workaround, so management-interface exposure is the only lever until you patch. TMUI should not be reachable from user networks, and this record is the argument for checking rather than assuming.
    • If you use ngx.fetch(): F5’s guidance is to “restrict ngx.fetch() destinations to trusted servers and avoid reading Response.statusText for responses from attacker-controlled or attacker-influenced endpoints.”

    Sourcing note

    Every CVE identifier, score, vector, affected range, and quoted description here comes from the CVE Program record API at cveawg.mitre.org and from NVD, both read on September 2, 2026. F5’s product security team is the assigning authority for all seven, so the descriptions and both CVSS vectors are F5’s own language and F5’s own grading, republished — there is no independent NVD analysis on these records yet, and no CWE assigned by anyone but F5.

    F5’s own knowledge-base articles — K000162599, K000162600, K000162601, K000162602, K000162603, and K000162521 — could not be read. my.f5.com returns a page whose body is a loading state; the advisory text is assembled in the browser and is not available to a non-browser fetch. That is the same pattern this site documented in HPE’s support portal on September 1. The version and remediation detail above is therefore recovered from the CVE records rather than read from the vendor page, and anything F5 said only in those articles is not reflected here.

    The njs 1.0.1 changelog was read from the project’s GitHub releases and corroborates all three njs entries, including the empty-reason-phrase trigger for CVE-2026-78222. That listing returned inconsistent release years to automated fetching, so no date is taken from it; the September 2, 2026 publication dates come from the CVE records.

    Unresolved: whether F5 grouped these into a quarterly security notification, and under what number, is not determinable from the records. No KEV entry exists for any of the seven, checked against NVD’s CISA fields, which lag the catalog by hours. No exploitation is claimed by F5 or observed in public reporting as of publication.