CWE-807
AllowedReliance on Untrusted Inputs in a Security Decision
Abstraction: Base · Status: Incomplete
The product uses a protection mechanism that relies on the existence or values of an input, but the input can be modified by an untrusted actor in a way that bypasses the protection mechanism.
200 vulnerabilities reference this CWE, most recent first.
GHSA-V226-32C7-X2V7
Vulnerability from github – Published: 2026-02-10 00:29 – Updated: 2026-02-10 02:57Impact
It is possible to make a specially crafted request with a valid API token that leads to privilege escalation.
Affected Versions:
≥= 0.27.19
Mitigation:
Upgrade to a patched version:
- 1.5.13 and later (regular release)
- 1.4.2 (active LTS release)
- 1.0.14 (end-of-life LTS release)
References
The issue was reported by our Core engineer, Dmitrii Patsura (@ovr), in our internal Slack and was promptly patched in a recent update.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@cubejs-backend/server-core"
},
"ranges": [
{
"events": [
{
"introduced": "0.27.19"
},
{
"fixed": "1.0.14"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@cubejs-backend/server-core"
},
"ranges": [
{
"events": [
{
"introduced": "1.1.0"
},
{
"fixed": "1.4.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@cubejs-backend/server-core"
},
"ranges": [
{
"events": [
{
"introduced": "1.5.0"
},
{
"fixed": "1.5.13"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-25958"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-10T00:29:07Z",
"nvd_published_at": "2026-02-09T23:16:06Z",
"severity": "HIGH"
},
"details": "### **Impact**\n\nIt is possible to make a specially crafted request with a valid API token that leads to privilege escalation.\n\n### Affected Versions:\n\n`\u2265= 0.27.19` \n\n### Mitigation:\n\nUpgrade to a patched version:\n\n- 1.5.13 and later (regular release)\n- 1.4.2 (active\u00a0[LTS release](https://cube.dev/docs/product/administration/distribution#long-term-support))\n- 1.0.14 (end-of-life LTS release)\n\n### **References**\n\nThe issue was reported by our Core engineer, Dmitrii Patsura (@ovr), in our internal Slack and was promptly patched in a recent update.",
"id": "GHSA-v226-32c7-x2v7",
"modified": "2026-02-10T02:57:34Z",
"published": "2026-02-10T00:29:07Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/cube-js/cube/security/advisories/GHSA-v226-32c7-x2v7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25958"
},
{
"type": "PACKAGE",
"url": "https://github.com/cube-js/cube"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Cube Core is vulnerable to privilege escalation via a specially crafted request"
}
GHSA-V25G-MVWR-F5FP
Vulnerability from github – Published: 2026-09-09 23:47 – Updated: 2026-09-09 23:47Description
Before 1.22, if the Basic Auth (htpasswd) middleware was not configured, all incoming HTTP headers were blindly forwarded to the webhook script execution environment as shell variables. While the Basic Auth middleware correctly strips the authentication header (X-WebAuthn-User) from the incoming request before conditionally re-injecting it on successful authentication, disabling Basic Auth left the system vulnerable if deployed behind an unhardened reverse proxy.
Impact
If an upstream reverse proxy is not properly hardened to strip client-provided authentication headers, an attacker could manually supply these headers (e.g., X-WebAuthn-User). A webhook script relying on this forwarded header for privilege elevation or identity verification could therefore be exploited to bypass security controls and impersonate other users.
Mitigation
The WHD_ALLOWED_UPSTREAM_HEADERS configuration setting has been introduced to enforce a strict allowlist of HTTP headers that can be converted into shell variables.
Additionally, the default behavior has been changed to adhere to the principle of least privilege. It is no longer * (allow all). The default allowed headers are now restricted to standard operational headers:
Accept,Content-Type,Content-Length,User-Agent,X-Forwarded-For
Administrators relying on upstream authentication proxies must explicitly add their authentication headers (e.g., WHD_ALLOWED_UPSTREAM_HEADERS="Accept,Content-Type,Content-Length,User-Agent,X-Forwarded-For,x-webauthn-user") to ensure they are passed to the scripts securely.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/ncarlier/webhookd"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.22.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-59157"
],
"database_specific": {
"cwe_ids": [
"CWE-290",
"CWE-807"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-09T23:47:03Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Description\nBefore 1.22, if the Basic Auth (`htpasswd`) middleware was not configured, all incoming HTTP headers were blindly forwarded to the webhook script execution environment as shell variables. While the Basic Auth middleware correctly strips the authentication header (`X-WebAuthn-User`) from the incoming request before conditionally re-injecting it on successful authentication, disabling Basic Auth left the system vulnerable if deployed behind an unhardened reverse proxy.\n\n## Impact\nIf an upstream reverse proxy is not properly hardened to strip client-provided authentication headers, an attacker could manually supply these headers (e.g., `X-WebAuthn-User`). A webhook script relying on this forwarded header for privilege elevation or identity verification could therefore be exploited to bypass security controls and impersonate other users.\n\n## Mitigation\nThe `WHD_ALLOWED_UPSTREAM_HEADERS` configuration setting has been introduced to enforce a strict allowlist of HTTP headers that can be converted into shell variables. \n\nAdditionally, the default behavior has been changed to adhere to the principle of least privilege. It is no longer `*` (allow all). The default allowed headers are now restricted to standard operational headers:\n`Accept,Content-Type,Content-Length,User-Agent,X-Forwarded-For`\n\nAdministrators relying on upstream authentication proxies must explicitly add their authentication headers (e.g., `WHD_ALLOWED_UPSTREAM_HEADERS=\"Accept,Content-Type,Content-Length,User-Agent,X-Forwarded-For,x-webauthn-user\"`) to ensure they are passed to the scripts securely.",
"id": "GHSA-v25g-mvwr-f5fp",
"modified": "2026-09-09T23:47:03Z",
"published": "2026-09-09T23:47:03Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ncarlier/webhookd/security/advisories/GHSA-v25g-mvwr-f5fp"
},
{
"type": "PACKAGE",
"url": "https://github.com/ncarlier/webhookd"
},
{
"type": "WEB",
"url": "https://github.com/ncarlier/webhookd/releases/tag/v1.22.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "webhookd: Unrestricted HTTP Header to Shell Variable Injection"
}
GHSA-V453-9P95-FP34
Vulnerability from github – Published: 2026-09-04 12:31 – Updated: 2026-09-04 12:31The Grav Form plugin (getgrav/grav-plugin-form) versions 8.0.6 through 9.1.19 select the reCAPTCHA version to validate based solely on which response field key is present in the submitted payload. On a site configured for reCAPTCHA v3, an anonymous attacker can place their v3 token under the v2 field name (g-recaptcha-response instead of token), causing validation to use the v2 branch, which never applies the score threshold or verifies the expected action. This results in a complete bypass of reCAPTCHA v3 bot protection. The issue is fixed in version 9.1.20.
{
"affected": [],
"aliases": [
"CVE-2026-85602"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-04T12:17:23Z",
"severity": "CRITICAL"
},
"details": "The Grav Form plugin (getgrav/grav-plugin-form) versions 8.0.6 through 9.1.19 select the reCAPTCHA version to validate based solely on which response field key is present in the submitted payload. On a site configured for reCAPTCHA v3, an anonymous attacker can place their v3 token under the v2 field name (g-recaptcha-response instead of token), causing validation to use the v2 branch, which never applies the score threshold or verifies the expected action. This results in a complete bypass of reCAPTCHA v3 bot protection. The issue is fixed in version 9.1.20.",
"id": "GHSA-v453-9p95-fp34",
"modified": "2026-09-04T12:31:01Z",
"published": "2026-09-04T12:31:01Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/security/advisories/GHSA-89j6-8h38-2cc3"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-85602"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/grav-form-plugin-before-9.1.20-recaptcha-v3-authentication-bypass"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-V5JF-VJFX-FRFR
Vulnerability from github – Published: 2026-04-02 09:30 – Updated: 2026-04-16 21:31SEPPmail Secure Email Gateway before version 15.0.3 allows an external user to modify GINA webdomain metadata and bypass per-domain restrictions.
{
"affected": [],
"aliases": [
"CVE-2026-29134"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-02T09:16:21Z",
"severity": "MODERATE"
},
"details": "SEPPmail Secure Email Gateway before version 15.0.3 allows an external user to modify GINA webdomain metadata and bypass per-domain restrictions.",
"id": "GHSA-v5jf-vjfx-frfr",
"modified": "2026-04-16T21:31:09Z",
"published": "2026-04-02T09:30:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-29134"
},
{
"type": "WEB",
"url": "https://downloads.seppmail.com/extrelnotes/150/ERN15.0.html#seppmail-vulnerability-disclosure-1503"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-V67P-PHPQ-FC8X
Vulnerability from github – Published: 2026-09-10 23:04 – Updated: 2026-09-10 23:04Summary
Traefik's entrypoint defenses against spoofed trusted header names — aliasHeadersStrategy / underscoreHeadersStrategy in delete or reject mode, and the default forwardedHeaders stripping of client-supplied X-Forwarded-* — scan req.Header only and never req.Trailer. An unauthenticated client can therefore smuggle a sanitized name (an aliasing spelling such as X_Auth_User, or a trusted name such as X-Forwarded-Prefix) as an HTTP/1.1 chunked trailer or an HTTP/2 trailer: reject does not return its documented 400, delete does not remove the name, and Traefik's reverse proxy forwarded the trailer to the backend — with an attacker-chosen value whenever a body-buffering middleware (the retry middleware with status codes, or the buffering middleware) reads the body before the proxy clone. Backends that merge trailers into their header namespace then act on the smuggled name. The fix stops forwarding request trailer values to the backend; the declared trailer names are still forwarded as permitted by RFC 9110 section 6.6.2.
Traefik v2 is not affected: the defect is in the custom reverse proxy introduced in v3 (pkg/proxy/httputil), and v2 uses the Go standard library's httputil.ReverseProxy, which does not forward request trailer values to the backend. Affected v3 lines from v3.2.0 through v3.7.12 include the end-of-life v3.2 through v3.6 lines, which will not receive a fix on their own line; the remedy for those users is to upgrade to v3.7.13.
Patches
- https://github.com/traefik/traefik/releases/tag/v3.7.13
For more information
If you have any questions or comments about this advisory, please open an issue.
Original Description ### Summary Traefik's entrypoint defenses against spoofed header names — `aliasHeadersStrategy` / `underscoreHeadersStrategy` in `delete` or `reject` mode, and the `forwardedHeaders` handling that strips client-supplied `X-Forwarded-*` — scan `req.Header` only and never `req.Trailer`, although the handlers' own comments promise to cover "header **and trailer**". An unauthenticated client can therefore deliver the aliasing name (`X_Auth_User`, `X.Auth.User`) or the trusted name itself (`X-Forwarded-Prefix`, …) as an HTTP/1.1 chunked trailer or an HTTP/2 trailer: `reject` does not return its documented `400`, `delete` does not remove the name, and the trailer form of an `X-Forwarded-*` name passes exactly where the header form is stripped. When a body-buffering middleware is in the chain (retry with `status` codes, or the `buffering` middleware — both measured), the trailer travels **with an attacker-chosen value**; measured end-to-end against the trailer-merging component Ubuntu 24.04 ships (pre-fix libevent, CVE-2026-63379), the header `X-Forwarded-Prefix: admin` is stripped and denied while the identical name as a trailer is acted upon as admin (`403 → 200`). On bare proxy paths only the trailer name travels (no value), bounding those deployments to name-level effects. ### Details **Root cause.** All four entrypoint handlers iterate `req.Header` only — the doc comments promise more than the code does (`pkg/server/server_entrypoint_tcp.go`):// removeAliasingHeaders removes any request header and trailer whose name contains a character
// which is neither a letter, a digit, nor a dash, as such a name aliases another header name.
func removeAliasingHeaders(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
for key := range req.Header { // ← req.Trailer is never scanned
if isAliasingHeaderName(key) {
delete(req.Header, key)
}
}
h.ServeHTTP(rw, req)
})
}
`rejectAliasingHeaders`, `removeHeadersWithUnderscores` and `rejectHeadersWithUnderscores` share the identical structure (the `reject` variants return `400` from the same loop). The sibling sanitization `forwardedheaders.DeleteXForwardedHeaders` (`pkg/middlewares/forwardedheaders/forwarded_header.go`) also scans `req.Header` only, so the trusted `X-Forwarded-*` names whose header form Traefik strips for untrusted clients — the managed `XHeadersSet`, which includes `X-Forwarded-Prefix` and `X-Forwarded-For` — survive in trailer form. Go's HTTP server populates `req.Trailer` from chunked/HTTP/2 trailers, and Traefik's proxy layer forwards those entries, bypassing the sanitization above.
**Contract provenance.** The "header and trailer" wording is in the original introducing diffs — `108a52644` (underscoreHeadersStrategy) and `0331801c` (aliasHeadersStrategy) — and is unchanged in `master` (full diff excerpts available on request). The option began as `allowHeadersWithUnderscores: false` (per the CVE-2026-54763 record) before becoming `underscoreHeadersStrategy` and then `aliasHeadersStrategy`. The user-facing documentation describes only "request headers".
**Mechanism (why names survive, and when values do too).**
1. *Name pre-fill at parse time.* The client's `Trailer: X_Auth_User` declaration makes Go's server move the declared keys into `req.Trailer` with nil values before the handler runs (`net/http/transfer.go`, `fixTrailer`); HTTP/2 does the same from the `trailer:` field in the initial HEADERS ("Setup Trailers", `net/http/internal/httpcommon/httpcommon.go`). The entrypoint handlers therefore cannot see the trailer name, but the proxy forwards it. Trailer keys are canonicalized with `textproto.CanonicalMIMEHeaderKey`, which treats dashes — not underscores — as case separators: the aliasing spelling survives canonicalization as e.g. `X_auth_user` (visible in the backend dumps in PoC §1) and remains detectable by `isAliasingHeaderName`, so the fix does not depend on the client's original spelling.
2. *Value survival depends on who reads the body first.* Trailer values are appended to `req.Trailer` only while the body is consumed (`readTrailer` / `copyTrailersToHandlerRequest`). On the bare path the reverse proxy calls `Request.Clone` at handler start, before any body read, so the clone captures nil values — on HTTP/1.1 the trailer field line is then omitted entirely (`net/http/header.go`, `Header.writeSubset` writes one line per value), and h2c delivers only the empty key. When a body-buffering middleware runs first, the order reverses: the retry middleware with `status` codes buffers the body via `mirror.NewReusableRequest` → `io.ReadAll(req.Body)` (`pkg/middlewares/retry/retry.go`, `pkg/server/service/loadbalancer/mirror/mirror.go`), the values are populated before `http.Request.Clone`, and they travel to the backend. Buffering triggers for idempotent methods with `status` alone; POST additionally requires `retryNonIdempotentMethod` (both measured). Retry and buffering are the two measured paths; the mirroring and failover services use the same `mirror.NewReusableRequest` helper (`pkg/server/service/loadbalancer/mirror/mirror.go`, `failover/failover.go` when `errors.status` is configured) and share its behavior (not measured). The `buffering` middleware drains the body eagerly before the proxy too: `pkg/middlewares/buffering/buffering.go` → oxy's `multibuf.New` → `ioutil.ReadAll` (github.com/mailgun/multibuf `buffer.go`; unset limits fall back to 1 MB `DefaultMemBytes`) — measured value-preserving with default limits.
3. *Undeclared trailers: transit depends on whether anything else was declared (measured).* On HTTP/2 the standard library server copies only pre-declared trailers ("Only copy it over it was pre-declared", `net/http/internal/http2/server.go`) — undeclared fields never appear. On HTTP/1.1 `readTrailer` parses the entire trailer section with no declaration filter, and `mergeSetHeader` either **rebinds** the map when nil (`*dst = src`) or blindly merges when non-nil (point 4). The rebind is why zero-declaration requests lose undeclared fields at Traefik's observability `req.WithContext` shallow copy (`pkg/middlewares/observability/observability.go`, `entrypoint.go`) — measured: they never leave the entrypoint even on buffered chains. But a **bait declaration** (any clean name, e.g. `X-Dummy`) keeps the map non-nil, and the blind merge then writes the undeclared field into the shared map at body EOF — measured on the retry-buffered chain: the backend receives `map[X-Dummy:[1] X_auth_user:[attacker-value]]` and presence-based policies flip; the bare path is unaffected and delivers only `map[X-Dummy:[]]`.
4. *Delete-mode stickiness depends on the merge semantics (measured).* `readTrailer` merges parsed trailer fields via `mergeSetHeader`, whose non-nil branch is a blind `maps.Copy` (`net/http/transfer.go`) — a key deleted by a handler is re-added **with its value** at body EOF. Measured on a bare Go server (`delete(r.Trailer, "X_auth_user")` before draining the body): HTTP/1.1 — `map[X_auth_user:[]]` → `map[X_auth_user:[attacker-value]]` (re-added); HTTP/2 — `map[X_auth_user:[]]` → `map[]` (stays deleted: `copyTrailersToHandlerRequest` checks the live map).
**Deliberate trailer-forwarding behavior (regression tests).** Traefik deliberately does not forward request trailers on the bare proxy chain, locked by the regression tests `pkg/proxy/httputil/trailer_test.go` and `pkg/proxy/fast/trailer_test.go` (added `86b5642f`, 2026-06-25; extended `d427dccf`, 2026-06-29): "trailers arrive after the body, once routing and security decisions have already been made, so forwarding them could raise security concerns in Traefik." The measured buffered-chain value survival (mechanism point 2) defeats exactly that locked invariant — the tests exercise only the bare chain — and the name-level h2c forwarding (empty keys) passes the tests' assertion (`Header.Get` is empty whether the key is absent or empty-valued): neither regression test catches this finding. The value-level path thus bypasses a deliberate, test-locked security invariant.
**Preconditions.**
1. An entrypoint whose sanitization is relied upon: `aliasHeadersStrategy` / `underscoreHeadersStrategy` set to `delete` or `reject`, or the default `forwardedHeaders` stripping of `X-Forwarded-*` for untrusted clients.
2. A request carrying the name as a declared trailer (HTTP/1.1 chunked, or HTTP/2), or — on HTTP/1.1 buffered chains only — as an undeclared trailer field riding a bait declaration (mechanism point 3).
3. For downstream impact: a backend that merges trailers into its header namespace (pre-fix libevent CVE-2026-63379 — still what Ubuntu 24.04 ships —, pre-fix blaze CVE-2026-73495, or custom code) or consumes trailer fields in a trust decision.
4. For the value-level path: the retry middleware with `status` codes, the `buffering` middleware, or another body-buffering middleware, in the chain.
**Precedent and scope.** This is the next variant of Traefik's own aliasing family — CVE-2026-33433 (GHSA-qr99-7898-vr7c), CVE-2026-39858 (GHSA-5m6w-wvh7-57vm), CVE-2026-54763 (GHSA-x677-9fxg-v5c5) — and Traefik's Security Decisions state the in-scope line: "a spelling that survives the entrypoint sanitisation and still reaches the backend as the trusted name". The trailer spelling is precisely that. The downstream merge class is cross-ecosystem: libevent CVE-2026-63379 (run live in PoC §3) and blaze/http4s CVE-2026-73495 (GHSA-46q4-43ph-c6fr, fixed `ef3e666`).
**Boundaries (measured).** Declaring `Content-Length`, `Transfer-Encoding` or `Trailer` as trailer fields is rejected with `400` by Go's server; `Host` and `Connection` pass through name-level. The FastProxy forwarding mode (opt-in `[experimental] fastProxy`) does not forward trailers; the default reverse-proxy path for `http://` backends does (PoC §3). HTTP/3 (quic-go) trailer semantics are untested. Undeclared trailers: HTTP/2 drops them entirely; on HTTP/1.1 they transit only via a bait declaration on buffered chains (mechanism point 3).
### PoC
Verified against a source-built Traefik (`master` @ `237f13c6`, built with **Go 1.27.0**; all harness backends built with Go 1.27.0 — the trailer behaviors cited in Details are version-sensitive `net/http` internals). Complete harness (clients, backends, configs, logs) available on request; the raw chunked requests below are HTTP/1.1 and reproducible with `nc`/`python`.
**1. Core bypass (`aliasHeadersStrategy = reject`).** Static config:
[entryPoints.web]
address = ":8090"
[entryPoints.web.http]
aliasHeadersStrategy = "reject"
[providers.file]
filename = "dynamic.toml"
watch = true
`dynamic.toml`: router `PathPrefix(`/`)` → service → `h2c://127.0.0.1:8081` (a Go echo backend that drains the body and prints `r.Trailer`). Requests (CRLF line endings; `5`/`0` are chunk sizes):
POST / HTTP/1.1
Host: 127.0.0.1:8090
Connection: close
Transfer-Encoding: chunked
Trailer: X_Auth_User
5
hello
0
X_Auth_User: attacker-value
Results:
header X_Auth_User (curl -H "X_Auth_User: x") → HTTP 400 (rejected, as designed)
trailer X_Auth_User (request above) → HTTP 200 (bypass: not rejected)
trailer X.Auth.User → HTTP 200 (bypass)
trailer X-Forwarded-Prefix → HTTP 200 (trusted-name trailer passes)
Backend evidence: `TRAILERS: map[X_auth_user:[]]`, `map[X.auth.user:[]]`, `map[X-Forwarded-Prefix:[]]`. The deprecated `underscoreHeadersStrategy = "reject"` behaves identically.
`aliasHeadersStrategy = "delete"` (same setup, `delete` in place of `reject`): header `X_Auth_User` / `X.Auth.User` → `200`, backend HEADERS contain neither (deleted, as designed); trailer `X_Auth_User` → `200`, backend `TRAILERS: map[X_auth_user:[]]` — **the trailer form survives `delete`**.
**2. Bare-path downstream semantics (name-level).** Same router, backend `h2c://127.0.0.1:8082` running a trailer-merging backend (trailers folded over headers, CGI-style name normalization — the CVE-2026-63379 pattern) that authorizes `/presence` on the merged key and `/value` on `X-Auth-User == "admin"`:
/presence, no trailer (control) → 403 DENIED
/presence, trailer X_Auth_User → 200 AUTHORIZED ← presence flip, empty value
/value, header X-Auth-User: admin + trailer → merged-user="" ← legitimate value erased
**3. Real CVE'd component flipped through Traefik — value-level.** Backend: Ubuntu 24.04's `libevent-2.1-7t64` 2.1.12-stable-9ubuntu2 (pre-fix; the merge was fixed only in 2.1.13) plus a small (≈100-line) `evhttp` server that authorizes via `evhttp_find_header(req->input_headers, ...)` (`/prefix` grants admin when `X-Forwarded-Prefix == "admin"`; server source available on request; build: `gcc server.c -levent`). Router adds the retry middleware:
[http.routers.lib]
entryPoints = ["web"]
rule = "PathPrefix(`/`)"
service = "lib"
middlewares = ["retry-lib"]
[http.middlewares.retry-lib.retry]
attempts = 2
status = ["500-599"]
[http.services.lib.loadBalancer.servers]
[http.services.lib.loadBalancer.servers.s1]
url = "http://127.0.0.1:8083"
Measured matrix (server log shows the merged `input_headers`):
| Request | Result through Traefik |
|---|---|
| header `X-Forwarded-Prefix: admin` | `403 DENIED` — stripped by `forwardedHeaders` |
| trailer `X-Forwarded-Prefix: admin` (chunked, declared; GET) | **`200 ADMIN (prefix=admin)`** — log: `X-Forwarded-Prefix: admin` merged |
| trailer `X_Auth_User: attacker-value` (GET) | **`200 AUTHORIZED (presence)`** — log: `X_auth_user: attacker-value` |
| same trailer request, retry middleware removed (clean restart) | `403 DENIED` — value dropped, field line omitted; log shows only `Trailer: X_auth_user` |
| same trailer request, direct to libevent (no Traefik) | `200 ADMIN (prefix=admin)` — CVE-2026-63379 baseline |
The value survives because the retry middleware buffers the body before the proxy clone (mechanism point 2). The same value path holds on h2c outbound (merge backend logs `trailer=map[X-Auth-User:[admin]]` → `200 AUTHORIZED (value=admin)`) and with the `buffering` middleware in place of retry (`/value` trailer → `200 AUTHORIZED (value=admin)`, `/xff` trailer → `200 ADMIN`).
**Bait declaration (measured).** Declaring a clean `Trailer: X-Dummy` while additionally sending the undeclared `X_Auth_User: attacker-value` in the trailer section: on the retry-buffered chain the backend receives `TRAILERS: map[X-Dummy:[1] X_auth_user:[attacker-value]]` → `200 AUTHORIZED` (presence policies flip on `X_auth_user`); the zero-declaration control still delivers `map[]`; the bare path delivers only `map[X-Dummy:[]]` (clone precedes the merge). Over HTTP/2 inbound with buffering (`client_h2c` through a retry chain with `retryNonIdempotentMethod`): backend `trailer=map[X_auth_user:[attacker-value]]` → `200 AUTHORIZED (presence)`.
**X-Forwarded-For IP-trust (same chain, measured).** Same router and retry middleware, backend `h2c://127.0.0.1:8082` running the merge backend with an added `/xff` route that grants access when the merged `X-Forwarded-For` equals `203.0.113.7` — the classic IP-allowlist pattern:
header X-Forwarded-For: 203.0.113.7 → 403 DENIED (xff)
backend log: merged XFF = "127.0.0.1" (Traefik stripped the client value and set its own)
trailer X-Forwarded-For: 203.0.113.7 (GET, retry) → 200 ADMIN (xff)
backend log: trailer=map[X-Forwarded-For:[203.0.113.7]]
trailer X-Forwarded-For: 203.0.113.7 (POST, retry without retryNonIdempotentMethod → not buffered) → 403 DENIED — merged XFF empty (bare-path value drop)
**4. Framing names and protocols.** Trailer `Content-Length, Host, Connection, Transfer-Encoding` → `400` (Go rejects); `Host, Connection` → `200`, backend `TRAILERS: map[Connection:[] Host:[]]`. HTTP/2 prior-knowledge client with trailer `X_Auth_User` → Traefik → h2c backend: `200`, backend `TRAILERS: map[X_auth_user:[]]` — same name-only outcome as PoC §2. HTTP/3 untested.
### Impact
**Kind of vulnerability.** A bypass of Traefik's documented defenses against spoofed trusted names. `reject` promises a `400` and `delete` promises removal for aliasing names; `forwardedHeaders` strips client-supplied `X-Forwarded-*` — and all of it applies to headers only, leaving the trailer channel open, with attacker-chosen values on body-buffering chains.
**Who is impacted.** Operators who enabled `delete`/`reject` to close the aliasing spoofing class (the documented mitigation for the CVE-2026-33433/39858/54763 family), and deployments whose backends trust `X-Forwarded-*` names or proxy-set identity headers — including the classic `X-Forwarded-For` IP-trust pattern, where Traefik strips the client's XFF from headers while the trailer form reaches trailer-merging backends. No opt-in option is required for the `X-Forwarded-*` path: the stripping is the default for untrusted clients. The value-level path additionally requires a body-buffering middleware (retry with `status` codes, or `buffering`) — mainstream documented features: the buffering middleware's documentation states that attaching it buffers the request body before forwarding, and the retry middleware's documentation example configures `status = ["400","500-599"]` — though no deployment telemetry is available to quantify their prevalence.
**Verified harm scenarios.**
1. *Broken protection contract.* Trailer-form aliasing names are neither rejected nor removed — the documented mitigation has a side door the operator believes is closed.
2. *Presence-based authorization bypass.* Trailer-merging upstreams authorizing on the presence of a trusted identity key flip their decision: `403 → 200 AUTHORIZED` through Traefik on an h2c merge backend (PoC §2) and on the real pre-fix libevent component (PoC §3).
3. *Value-level identity spoofing.* On body-buffering chains the trailer carries the attacker's value: `X-Forwarded-Prefix: admin` delivered through Traefik authorizes as admin on the real CVE'd merge backend, while the identical header form is stripped and denied (PoC §3) — the CVE-2026-63379-class value injection chained through Traefik's own value-preserving middleware behavior.
4. *Legitimate identity value erased.* A trailer-merging upstream folds the empty trailer over the identity header — `X-Auth-User: admin` becomes empty in the merged view (PoC §2). This typically denies rather than grants; its relevance is the erasure primitive and availability of the legitimate identity.
5. *Routing-header name channel.* `Host` and `Connection` trailer fields pass Go's validation and reach the backend name-level (PoC §4); a trailer-merging upstream's virtual-host view is overwritten with an empty value.
**Explicitly out of scope (verified).** Value delivery requires a body-buffering middleware in the chain — on bare proxy paths values are dropped (PoC §3); the FastProxy path does not forward trailers; `Content-Length`/`Transfer-Encoding`/`Trailer` trailer fields are rejected with `400`.
### Recommended fix
Make the four entrypoint handlers and `forwardedheaders.DeleteXForwardedHeaders` iterate `req.Trailer` as well as `req.Header` — deleting matching trailer entries in `delete` mode and returning `400` in `reject` mode — at the exact place the header filtering already happens. The entrypoint stage sees every declared name (pre-filled before the handler) and covers all of HTTP/2 (undeclared fields are dropped by the stdlib server — mechanism point 3); `reject` returns `400` for those. On HTTP/1.1 buffered chains, names that appear only at body EOF — undeclared fields riding a bait declaration (mechanism point 3) and deleted keys re-added by the blind `mergeSetHeader` merge (mechanism point 4) — bypass the entrypoint stage, so the sanitization must be re-applied after the body's final read for **both** modes and for `DeleteXForwardedHeaders`; at that point the request may already be partially forwarded, so the second stage strips rather than rejects — `reject` deployments get delete-semantics for the late names. HTTP/3 (quic-go) may not pre-fill declared trailer keys before the handler at all (untested); there the post-body stage is the only certain defense. The fix sanitizes only the names the operator's policy targets — it does not drop the trailer channel, so legitimate trailers such as gRPC's `grpc-status` are unaffected.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/traefik/traefik/v3"
},
"ranges": [
{
"events": [
{
"introduced": "3.2.0"
},
{
"fixed": "3.7.13"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-88004"
],
"database_specific": {
"cwe_ids": [
"CWE-436",
"CWE-807"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-10T23:04:21Z",
"nvd_published_at": "2026-09-10T15:17:54Z",
"severity": "HIGH"
},
"details": "## Summary\n\nTraefik\u0027s entrypoint defenses against spoofed trusted header names \u2014 `aliasHeadersStrategy` / `underscoreHeadersStrategy` in `delete` or `reject` mode, and the default `forwardedHeaders` stripping of client-supplied `X-Forwarded-*` \u2014 scan `req.Header` only and never `req.Trailer`. An unauthenticated client can therefore smuggle a sanitized name (an aliasing spelling such as `X_Auth_User`, or a trusted name such as `X-Forwarded-Prefix`) as an HTTP/1.1 chunked trailer or an HTTP/2 trailer: `reject` does not return its documented `400`, `delete` does not remove the name, and Traefik\u0027s reverse proxy forwarded the trailer to the backend \u2014 with an attacker-chosen value whenever a body-buffering middleware (the `retry` middleware with status codes, or the `buffering` middleware) reads the body before the proxy clone. Backends that merge trailers into their header namespace then act on the smuggled name. The fix stops forwarding request trailer values to the backend; the declared trailer names are still forwarded as permitted by RFC 9110 section 6.6.2.\n\nTraefik v2 is not affected: the defect is in the custom reverse proxy introduced in v3 (`pkg/proxy/httputil`), and v2 uses the Go standard library\u0027s `httputil.ReverseProxy`, which does not forward request trailer values to the backend. Affected v3 lines from v3.2.0 through v3.7.12 include the end-of-life v3.2 through v3.6 lines, which will not receive a fix on their own line; the remedy for those users is to upgrade to v3.7.13.\n\n## Patches\n\n- https://github.com/traefik/traefik/releases/tag/v3.7.13\n\n## For more information\n\nIf you have any questions or comments about this advisory, please [open an issue](https://github.com/traefik/traefik/issues).\n\n\u003cdetails\u003e\n\u003csummary\u003eOriginal Description\u003c/summary\u003e\n\n### Summary\n\nTraefik\u0027s entrypoint defenses against spoofed header names \u2014 `aliasHeadersStrategy` / `underscoreHeadersStrategy` in `delete` or `reject` mode, and the `forwardedHeaders` handling that strips client-supplied `X-Forwarded-*` \u2014 scan `req.Header` only and never `req.Trailer`, although the handlers\u0027 own comments promise to cover \"header **and trailer**\". An unauthenticated client can therefore deliver the aliasing name (`X_Auth_User`, `X.Auth.User`) or the trusted name itself (`X-Forwarded-Prefix`, \u2026) as an HTTP/1.1 chunked trailer or an HTTP/2 trailer: `reject` does not return its documented `400`, `delete` does not remove the name, and the trailer form of an `X-Forwarded-*` name passes exactly where the header form is stripped. When a body-buffering middleware is in the chain (retry with `status` codes, or the `buffering` middleware \u2014 both measured), the trailer travels **with an attacker-chosen value**; measured end-to-end against the trailer-merging component Ubuntu 24.04 ships (pre-fix libevent, CVE-2026-63379), the header `X-Forwarded-Prefix: admin` is stripped and denied while the identical name as a trailer is acted upon as admin (`403 \u2192 200`). On bare proxy paths only the trailer name travels (no value), bounding those deployments to name-level effects.\n\n### Details\n\n**Root cause.** All four entrypoint handlers iterate `req.Header` only \u2014 the doc comments promise more than the code does (`pkg/server/server_entrypoint_tcp.go`):\n\n```go\n// removeAliasingHeaders removes any request header and trailer whose name contains a character\n// which is neither a letter, a digit, nor a dash, as such a name aliases another header name.\nfunc removeAliasingHeaders(h http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {\n\t\tfor key := range req.Header { // \u2190 req.Trailer is never scanned\n\t\t\tif isAliasingHeaderName(key) {\n\t\t\t\tdelete(req.Header, key)\n\t\t\t}\n\t\t}\n\t\th.ServeHTTP(rw, req)\n\t})\n}\n```\n\n`rejectAliasingHeaders`, `removeHeadersWithUnderscores` and `rejectHeadersWithUnderscores` share the identical structure (the `reject` variants return `400` from the same loop). The sibling sanitization `forwardedheaders.DeleteXForwardedHeaders` (`pkg/middlewares/forwardedheaders/forwarded_header.go`) also scans `req.Header` only, so the trusted `X-Forwarded-*` names whose header form Traefik strips for untrusted clients \u2014 the managed `XHeadersSet`, which includes `X-Forwarded-Prefix` and `X-Forwarded-For` \u2014 survive in trailer form. Go\u0027s HTTP server populates `req.Trailer` from chunked/HTTP/2 trailers, and Traefik\u0027s proxy layer forwards those entries, bypassing the sanitization above.\n\n**Contract provenance.** The \"header and trailer\" wording is in the original introducing diffs \u2014 `108a52644` (underscoreHeadersStrategy) and `0331801c` (aliasHeadersStrategy) \u2014 and is unchanged in `master` (full diff excerpts available on request). The option began as `allowHeadersWithUnderscores: false` (per the CVE-2026-54763 record) before becoming `underscoreHeadersStrategy` and then `aliasHeadersStrategy`. The user-facing documentation describes only \"request headers\".\n\n**Mechanism (why names survive, and when values do too).**\n\n1. *Name pre-fill at parse time.* The client\u0027s `Trailer: X_Auth_User` declaration makes Go\u0027s server move the declared keys into `req.Trailer` with nil values before the handler runs (`net/http/transfer.go`, `fixTrailer`); HTTP/2 does the same from the `trailer:` field in the initial HEADERS (\"Setup Trailers\", `net/http/internal/httpcommon/httpcommon.go`). The entrypoint handlers therefore cannot see the trailer name, but the proxy forwards it. Trailer keys are canonicalized with `textproto.CanonicalMIMEHeaderKey`, which treats dashes \u2014 not underscores \u2014 as case separators: the aliasing spelling survives canonicalization as e.g. `X_auth_user` (visible in the backend dumps in PoC \u00a71) and remains detectable by `isAliasingHeaderName`, so the fix does not depend on the client\u0027s original spelling.\n2. *Value survival depends on who reads the body first.* Trailer values are appended to `req.Trailer` only while the body is consumed (`readTrailer` / `copyTrailersToHandlerRequest`). On the bare path the reverse proxy calls `Request.Clone` at handler start, before any body read, so the clone captures nil values \u2014 on HTTP/1.1 the trailer field line is then omitted entirely (`net/http/header.go`, `Header.writeSubset` writes one line per value), and h2c delivers only the empty key. When a body-buffering middleware runs first, the order reverses: the retry middleware with `status` codes buffers the body via `mirror.NewReusableRequest` \u2192 `io.ReadAll(req.Body)` (`pkg/middlewares/retry/retry.go`, `pkg/server/service/loadbalancer/mirror/mirror.go`), the values are populated before `http.Request.Clone`, and they travel to the backend. Buffering triggers for idempotent methods with `status` alone; POST additionally requires `retryNonIdempotentMethod` (both measured). Retry and buffering are the two measured paths; the mirroring and failover services use the same `mirror.NewReusableRequest` helper (`pkg/server/service/loadbalancer/mirror/mirror.go`, `failover/failover.go` when `errors.status` is configured) and share its behavior (not measured). The `buffering` middleware drains the body eagerly before the proxy too: `pkg/middlewares/buffering/buffering.go` \u2192 oxy\u0027s `multibuf.New` \u2192 `ioutil.ReadAll` (github.com/mailgun/multibuf `buffer.go`; unset limits fall back to 1 MB `DefaultMemBytes`) \u2014 measured value-preserving with default limits.\n3. *Undeclared trailers: transit depends on whether anything else was declared (measured).* On HTTP/2 the standard library server copies only pre-declared trailers (\"Only copy it over it was pre-declared\", `net/http/internal/http2/server.go`) \u2014 undeclared fields never appear. On HTTP/1.1 `readTrailer` parses the entire trailer section with no declaration filter, and `mergeSetHeader` either **rebinds** the map when nil (`*dst = src`) or blindly merges when non-nil (point 4). The rebind is why zero-declaration requests lose undeclared fields at Traefik\u0027s observability `req.WithContext` shallow copy (`pkg/middlewares/observability/observability.go`, `entrypoint.go`) \u2014 measured: they never leave the entrypoint even on buffered chains. But a **bait declaration** (any clean name, e.g. `X-Dummy`) keeps the map non-nil, and the blind merge then writes the undeclared field into the shared map at body EOF \u2014 measured on the retry-buffered chain: the backend receives `map[X-Dummy:[1] X_auth_user:[attacker-value]]` and presence-based policies flip; the bare path is unaffected and delivers only `map[X-Dummy:[]]`.\n4. *Delete-mode stickiness depends on the merge semantics (measured).* `readTrailer` merges parsed trailer fields via `mergeSetHeader`, whose non-nil branch is a blind `maps.Copy` (`net/http/transfer.go`) \u2014 a key deleted by a handler is re-added **with its value** at body EOF. Measured on a bare Go server (`delete(r.Trailer, \"X_auth_user\")` before draining the body): HTTP/1.1 \u2014 `map[X_auth_user:[]]` \u2192 `map[X_auth_user:[attacker-value]]` (re-added); HTTP/2 \u2014 `map[X_auth_user:[]]` \u2192 `map[]` (stays deleted: `copyTrailersToHandlerRequest` checks the live map).\n\n**Deliberate trailer-forwarding behavior (regression tests).** Traefik deliberately does not forward request trailers on the bare proxy chain, locked by the regression tests `pkg/proxy/httputil/trailer_test.go` and `pkg/proxy/fast/trailer_test.go` (added `86b5642f`, 2026-06-25; extended `d427dccf`, 2026-06-29): \"trailers arrive after the body, once routing and security decisions have already been made, so forwarding them could raise security concerns in Traefik.\" The measured buffered-chain value survival (mechanism point 2) defeats exactly that locked invariant \u2014 the tests exercise only the bare chain \u2014 and the name-level h2c forwarding (empty keys) passes the tests\u0027 assertion (`Header.Get` is empty whether the key is absent or empty-valued): neither regression test catches this finding. The value-level path thus bypasses a deliberate, test-locked security invariant.\n\n**Preconditions.**\n\n1. An entrypoint whose sanitization is relied upon: `aliasHeadersStrategy` / `underscoreHeadersStrategy` set to `delete` or `reject`, or the default `forwardedHeaders` stripping of `X-Forwarded-*` for untrusted clients.\n2. A request carrying the name as a declared trailer (HTTP/1.1 chunked, or HTTP/2), or \u2014 on HTTP/1.1 buffered chains only \u2014 as an undeclared trailer field riding a bait declaration (mechanism point 3).\n3. For downstream impact: a backend that merges trailers into its header namespace (pre-fix libevent CVE-2026-63379 \u2014 still what Ubuntu 24.04 ships \u2014, pre-fix blaze CVE-2026-73495, or custom code) or consumes trailer fields in a trust decision.\n4. For the value-level path: the retry middleware with `status` codes, the `buffering` middleware, or another body-buffering middleware, in the chain.\n\n**Precedent and scope.** This is the next variant of Traefik\u0027s own aliasing family \u2014 CVE-2026-33433 (GHSA-qr99-7898-vr7c), CVE-2026-39858 (GHSA-5m6w-wvh7-57vm), CVE-2026-54763 (GHSA-x677-9fxg-v5c5) \u2014 and Traefik\u0027s Security Decisions state the in-scope line: \"a spelling that survives the entrypoint sanitisation and still reaches the backend as the trusted name\". The trailer spelling is precisely that. The downstream merge class is cross-ecosystem: libevent CVE-2026-63379 (run live in PoC \u00a73) and blaze/http4s CVE-2026-73495 (GHSA-46q4-43ph-c6fr, fixed `ef3e666`).\n\n**Boundaries (measured).** Declaring `Content-Length`, `Transfer-Encoding` or `Trailer` as trailer fields is rejected with `400` by Go\u0027s server; `Host` and `Connection` pass through name-level. The FastProxy forwarding mode (opt-in `[experimental] fastProxy`) does not forward trailers; the default reverse-proxy path for `http://` backends does (PoC \u00a73). HTTP/3 (quic-go) trailer semantics are untested. Undeclared trailers: HTTP/2 drops them entirely; on HTTP/1.1 they transit only via a bait declaration on buffered chains (mechanism point 3).\n\n### PoC\n\nVerified against a source-built Traefik (`master` @ `237f13c6`, built with **Go 1.27.0**; all harness backends built with Go 1.27.0 \u2014 the trailer behaviors cited in Details are version-sensitive `net/http` internals). Complete harness (clients, backends, configs, logs) available on request; the raw chunked requests below are HTTP/1.1 and reproducible with `nc`/`python`.\n\n**1. Core bypass (`aliasHeadersStrategy = reject`).** Static config:\n\n```toml\n[entryPoints.web]\n address = \":8090\"\n [entryPoints.web.http]\n aliasHeadersStrategy = \"reject\"\n\n[providers.file]\n filename = \"dynamic.toml\"\n watch = true\n```\n\n`dynamic.toml`: router `PathPrefix(`/`)` \u2192 service \u2192 `h2c://127.0.0.1:8081` (a Go echo backend that drains the body and prints `r.Trailer`). Requests (CRLF line endings; `5`/`0` are chunk sizes):\n\n```\nPOST / HTTP/1.1\nHost: 127.0.0.1:8090\nConnection: close\nTransfer-Encoding: chunked\nTrailer: X_Auth_User\n\n5\nhello\n0\nX_Auth_User: attacker-value\n\n```\n\nResults:\n\n```\nheader X_Auth_User (curl -H \"X_Auth_User: x\") \u2192 HTTP 400 (rejected, as designed)\ntrailer X_Auth_User (request above) \u2192 HTTP 200 (bypass: not rejected)\ntrailer X.Auth.User \u2192 HTTP 200 (bypass)\ntrailer X-Forwarded-Prefix \u2192 HTTP 200 (trusted-name trailer passes)\n```\n\nBackend evidence: `TRAILERS: map[X_auth_user:[]]`, `map[X.auth.user:[]]`, `map[X-Forwarded-Prefix:[]]`. The deprecated `underscoreHeadersStrategy = \"reject\"` behaves identically.\n\n`aliasHeadersStrategy = \"delete\"` (same setup, `delete` in place of `reject`): header `X_Auth_User` / `X.Auth.User` \u2192 `200`, backend HEADERS contain neither (deleted, as designed); trailer `X_Auth_User` \u2192 `200`, backend `TRAILERS: map[X_auth_user:[]]` \u2014 **the trailer form survives `delete`**.\n\n**2. Bare-path downstream semantics (name-level).** Same router, backend `h2c://127.0.0.1:8082` running a trailer-merging backend (trailers folded over headers, CGI-style name normalization \u2014 the CVE-2026-63379 pattern) that authorizes `/presence` on the merged key and `/value` on `X-Auth-User == \"admin\"`:\n\n```\n/presence, no trailer (control) \u2192 403 DENIED\n/presence, trailer X_Auth_User \u2192 200 AUTHORIZED \u2190 presence flip, empty value\n/value, header X-Auth-User: admin + trailer \u2192 merged-user=\"\" \u2190 legitimate value erased\n```\n\n**3. Real CVE\u0027d component flipped through Traefik \u2014 value-level.** Backend: Ubuntu 24.04\u0027s `libevent-2.1-7t64` 2.1.12-stable-9ubuntu2 (pre-fix; the merge was fixed only in 2.1.13) plus a small (\u2248100-line) `evhttp` server that authorizes via `evhttp_find_header(req-\u003einput_headers, ...)` (`/prefix` grants admin when `X-Forwarded-Prefix == \"admin\"`; server source available on request; build: `gcc server.c -levent`). Router adds the retry middleware:\n\n```toml\n[http.routers.lib]\n entryPoints = [\"web\"]\n rule = \"PathPrefix(`/`)\"\n service = \"lib\"\n middlewares = [\"retry-lib\"]\n\n[http.middlewares.retry-lib.retry]\n attempts = 2\n status = [\"500-599\"]\n\n[http.services.lib.loadBalancer.servers]\n [http.services.lib.loadBalancer.servers.s1]\n url = \"http://127.0.0.1:8083\"\n```\n\nMeasured matrix (server log shows the merged `input_headers`):\n\n| Request | Result through Traefik |\n|---|---|\n| header `X-Forwarded-Prefix: admin` | `403 DENIED` \u2014 stripped by `forwardedHeaders` |\n| trailer `X-Forwarded-Prefix: admin` (chunked, declared; GET) | **`200 ADMIN (prefix=admin)`** \u2014 log: `X-Forwarded-Prefix: admin` merged |\n| trailer `X_Auth_User: attacker-value` (GET) | **`200 AUTHORIZED (presence)`** \u2014 log: `X_auth_user: attacker-value` |\n| same trailer request, retry middleware removed (clean restart) | `403 DENIED` \u2014 value dropped, field line omitted; log shows only `Trailer: X_auth_user` |\n| same trailer request, direct to libevent (no Traefik) | `200 ADMIN (prefix=admin)` \u2014 CVE-2026-63379 baseline |\n\nThe value survives because the retry middleware buffers the body before the proxy clone (mechanism point 2). The same value path holds on h2c outbound (merge backend logs `trailer=map[X-Auth-User:[admin]]` \u2192 `200 AUTHORIZED (value=admin)`) and with the `buffering` middleware in place of retry (`/value` trailer \u2192 `200 AUTHORIZED (value=admin)`, `/xff` trailer \u2192 `200 ADMIN`).\n\n**Bait declaration (measured).** Declaring a clean `Trailer: X-Dummy` while additionally sending the undeclared `X_Auth_User: attacker-value` in the trailer section: on the retry-buffered chain the backend receives `TRAILERS: map[X-Dummy:[1] X_auth_user:[attacker-value]]` \u2192 `200 AUTHORIZED` (presence policies flip on `X_auth_user`); the zero-declaration control still delivers `map[]`; the bare path delivers only `map[X-Dummy:[]]` (clone precedes the merge). Over HTTP/2 inbound with buffering (`client_h2c` through a retry chain with `retryNonIdempotentMethod`): backend `trailer=map[X_auth_user:[attacker-value]]` \u2192 `200 AUTHORIZED (presence)`.\n\n**X-Forwarded-For IP-trust (same chain, measured).** Same router and retry middleware, backend `h2c://127.0.0.1:8082` running the merge backend with an added `/xff` route that grants access when the merged `X-Forwarded-For` equals `203.0.113.7` \u2014 the classic IP-allowlist pattern:\n\n```\nheader X-Forwarded-For: 203.0.113.7 \u2192 403 DENIED (xff)\n backend log: merged XFF = \"127.0.0.1\" (Traefik stripped the client value and set its own)\ntrailer X-Forwarded-For: 203.0.113.7 (GET, retry) \u2192 200 ADMIN (xff)\n backend log: trailer=map[X-Forwarded-For:[203.0.113.7]]\ntrailer X-Forwarded-For: 203.0.113.7 (POST, retry without retryNonIdempotentMethod \u2192 not buffered) \u2192 403 DENIED \u2014 merged XFF empty (bare-path value drop)\n```\n\n**4. Framing names and protocols.** Trailer `Content-Length, Host, Connection, Transfer-Encoding` \u2192 `400` (Go rejects); `Host, Connection` \u2192 `200`, backend `TRAILERS: map[Connection:[] Host:[]]`. HTTP/2 prior-knowledge client with trailer `X_Auth_User` \u2192 Traefik \u2192 h2c backend: `200`, backend `TRAILERS: map[X_auth_user:[]]` \u2014 same name-only outcome as PoC \u00a72. HTTP/3 untested.\n\n### Impact\n\n**Kind of vulnerability.** A bypass of Traefik\u0027s documented defenses against spoofed trusted names. `reject` promises a `400` and `delete` promises removal for aliasing names; `forwardedHeaders` strips client-supplied `X-Forwarded-*` \u2014 and all of it applies to headers only, leaving the trailer channel open, with attacker-chosen values on body-buffering chains.\n\n**Who is impacted.** Operators who enabled `delete`/`reject` to close the aliasing spoofing class (the documented mitigation for the CVE-2026-33433/39858/54763 family), and deployments whose backends trust `X-Forwarded-*` names or proxy-set identity headers \u2014 including the classic `X-Forwarded-For` IP-trust pattern, where Traefik strips the client\u0027s XFF from headers while the trailer form reaches trailer-merging backends. No opt-in option is required for the `X-Forwarded-*` path: the stripping is the default for untrusted clients. The value-level path additionally requires a body-buffering middleware (retry with `status` codes, or `buffering`) \u2014 mainstream documented features: the buffering middleware\u0027s documentation states that attaching it buffers the request body before forwarding, and the retry middleware\u0027s documentation example configures `status = [\"400\",\"500-599\"]` \u2014 though no deployment telemetry is available to quantify their prevalence.\n\n**Verified harm scenarios.**\n\n1. *Broken protection contract.* Trailer-form aliasing names are neither rejected nor removed \u2014 the documented mitigation has a side door the operator believes is closed.\n2. *Presence-based authorization bypass.* Trailer-merging upstreams authorizing on the presence of a trusted identity key flip their decision: `403 \u2192 200 AUTHORIZED` through Traefik on an h2c merge backend (PoC \u00a72) and on the real pre-fix libevent component (PoC \u00a73).\n3. *Value-level identity spoofing.* On body-buffering chains the trailer carries the attacker\u0027s value: `X-Forwarded-Prefix: admin` delivered through Traefik authorizes as admin on the real CVE\u0027d merge backend, while the identical header form is stripped and denied (PoC \u00a73) \u2014 the CVE-2026-63379-class value injection chained through Traefik\u0027s own value-preserving middleware behavior.\n4. *Legitimate identity value erased.* A trailer-merging upstream folds the empty trailer over the identity header \u2014 `X-Auth-User: admin` becomes empty in the merged view (PoC \u00a72). This typically denies rather than grants; its relevance is the erasure primitive and availability of the legitimate identity.\n5. *Routing-header name channel.* `Host` and `Connection` trailer fields pass Go\u0027s validation and reach the backend name-level (PoC \u00a74); a trailer-merging upstream\u0027s virtual-host view is overwritten with an empty value.\n\n**Explicitly out of scope (verified).** Value delivery requires a body-buffering middleware in the chain \u2014 on bare proxy paths values are dropped (PoC \u00a73); the FastProxy path does not forward trailers; `Content-Length`/`Transfer-Encoding`/`Trailer` trailer fields are rejected with `400`.\n\n### Recommended fix\n\nMake the four entrypoint handlers and `forwardedheaders.DeleteXForwardedHeaders` iterate `req.Trailer` as well as `req.Header` \u2014 deleting matching trailer entries in `delete` mode and returning `400` in `reject` mode \u2014 at the exact place the header filtering already happens. The entrypoint stage sees every declared name (pre-filled before the handler) and covers all of HTTP/2 (undeclared fields are dropped by the stdlib server \u2014 mechanism point 3); `reject` returns `400` for those. On HTTP/1.1 buffered chains, names that appear only at body EOF \u2014 undeclared fields riding a bait declaration (mechanism point 3) and deleted keys re-added by the blind `mergeSetHeader` merge (mechanism point 4) \u2014 bypass the entrypoint stage, so the sanitization must be re-applied after the body\u0027s final read for **both** modes and for `DeleteXForwardedHeaders`; at that point the request may already be partially forwarded, so the second stage strips rather than rejects \u2014 `reject` deployments get delete-semantics for the late names. HTTP/3 (quic-go) may not pre-fill declared trailer keys before the handler at all (untested); there the post-body stage is the only certain defense. The fix sanitizes only the names the operator\u0027s policy targets \u2014 it does not drop the trailer channel, so legitimate trailers such as gRPC\u0027s `grpc-status` are unaffected.\n\n\u003c/details\u003e\n\n---",
"id": "GHSA-v67p-phpq-fc8x",
"modified": "2026-09-10T23:04:21Z",
"published": "2026-09-10T23:04:21Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/traefik/traefik/security/advisories/GHSA-v67p-phpq-fc8x"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-88004"
},
{
"type": "WEB",
"url": "https://github.com/traefik/traefik/pull/13822"
},
{
"type": "WEB",
"url": "https://github.com/traefik/traefik/commit/55bbda4f65e0f9c533c870983a767a7081126db7"
},
{
"type": "PACKAGE",
"url": "https://github.com/traefik/traefik"
},
{
"type": "WEB",
"url": "https://github.com/traefik/traefik/releases/tag/v3.7.13"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Traefik entrypoint header-name sanitization bypassed via request trailers"
}
GHSA-V72C-JRWC-X8QX
Vulnerability from github – Published: 2026-08-12 00:31 – Updated: 2026-08-12 00:31The Mira cloud API accepts the firmware version reported by the companion app as authoritative for a given device, without independently attesting the version from the device itself. An authenticated attacker could submit arbitrary firmware version strings for their own device, allowing them to evade vendor-side vulnerable-fleet analytics, suppress security update prompts to the user, and misrepresent patch-adoption metrics.
{
"affected": [],
"aliases": [
"CVE-2026-64934"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-11T22:18:41Z",
"severity": "MODERATE"
},
"details": "The Mira cloud API accepts the firmware version reported by the companion app as authoritative for a given device, without independently attesting the version from the device itself. An authenticated attacker could submit arbitrary firmware version strings for their own device, allowing them to evade vendor-side vulnerable-fleet analytics, suppress security update prompts to the user, and misrepresent patch-adoption metrics.",
"id": "GHSA-v72c-jrwc-x8qx",
"modified": "2026-08-12T00:31:11Z",
"published": "2026-08-12T00:31:11Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-64934"
},
{
"type": "WEB",
"url": "https://github.com/cisagov/CSAF/blob/develop/csaf_files/OT/white/2026/icsma-26-223-01.json"
},
{
"type": "WEB",
"url": "https://www.cisa.gov/news-events/ics-medical-advisories/icsma-26-223-01"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-VHJM-W3VW-G6JW
Vulnerability from github – Published: 2025-03-12 21:31 – Updated: 2025-03-12 21:31A reliance on untrusted input for a security decision in the GlobalProtect app on Windows devices potentially enables a locally authenticated non-administrative Windows user to escalate their privileges to NT AUTHORITY\SYSTEM.
GlobalProtect App on macOS, Linux, iOS, Android, Chrome OS and GlobalProtect UWP App are not affected.
{
"affected": [],
"aliases": [
"CVE-2025-0117"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-03-12T19:15:37Z",
"severity": "HIGH"
},
"details": "A reliance on untrusted input for a security decision in the GlobalProtect app on Windows devices potentially enables a locally authenticated non-administrative Windows user to escalate their privileges to NT AUTHORITY\\SYSTEM.\n\nGlobalProtect App on macOS, Linux, iOS, Android, Chrome OS and GlobalProtect UWP App are not affected.",
"id": "GHSA-vhjm-w3vw-g6jw",
"modified": "2025-03-12T21:31:29Z",
"published": "2025-03-12T21:31:29Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-0117"
},
{
"type": "WEB",
"url": "https://security.paloaltonetworks.com/CVE-2025-0117"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:P/VC:N/VI:H/VA:N/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:N/R:U/V:D/RE:M/U:Amber",
"type": "CVSS_V4"
}
]
}
GHSA-VJ7P-X3WH-G79X
Vulnerability from github – Published: 2026-03-24 15:30 – Updated: 2026-03-24 15:30Pixel Studio 2.17 contains a denial of service vulnerability that allows local attackers to crash the application by providing malformed input through the keyboard interface. Attackers can trigger the vulnerability by entering arbitrary characters, causing the application to become unresponsive or terminate abnormally.
{
"affected": [],
"aliases": [
"CVE-2019-25621"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-03-23T14:16:26Z",
"severity": "MODERATE"
},
"details": "Pixel Studio 2.17 contains a denial of service vulnerability that allows local attackers to crash the application by providing malformed input through the keyboard interface. Attackers can trigger the vulnerability by entering arbitrary characters, causing the application to become unresponsive or terminate abnormally.",
"id": "GHSA-vj7p-x3wh-g79x",
"modified": "2026-03-24T15:30:26Z",
"published": "2026-03-24T15:30:26Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-25621"
},
{
"type": "WEB",
"url": "https://www.exploit-db.com/exploits/46127"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/pixel-studio-denial-of-service-via-malformed-input"
},
{
"type": "WEB",
"url": "http://www.pixarra.com"
},
{
"type": "WEB",
"url": "http://www.pixarra.com/uploads/9/4/6/3/94635436/tbpixelstudio_install.exe"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-VRGW-VGHM-5P74
Vulnerability from github – Published: 2026-09-21 12:32 – Updated: 2026-09-21 12:32Temporal Server decided whether a Workflow completion callback was internal by reading a caller-supplied HTTP header. An authenticated caller holding only write permission in a single namespace could attach a completion callback whose URL host matched the configured callback address allowlist, whose URL path was any Temporal HTTP API route, and whose header map contained a non-empty header named source. When the History service delivered that callback, the non-empty source header caused it to re-target the request at the local frontend client and rewrite only the scheme and host, preserving the caller's path, query, and request body. Where an internal frontend is deployed with its HTTP API enabled, that client resolves to the internal frontend, which authorizes every request as a system administrator without requiring authentication information. The result is that the server performs an attacker-chosen state-changing HTTP POST against its own administrative API on the caller's behalf, in namespaces where the caller has no permission. The caller never needs network access to the internal frontend, because the History service makes the request. Confirmed effects include terminating Workflows in other namespaces, registering namespaces, modifying another namespace's configuration, and deleting another namespace and its Workflows. The affected routing logic is present in both the HSM and CHASM callback delivery implementations. This description and the CVSS score in this record describe releases 1.30.0 and later, where any non-empty source header is sufficient. Releases 1.25.0 through 1.29.7 are affected by a narrower form of the same defect in which the header must exactly match a configured cluster ID, a UUID that a namespace-scoped caller cannot read through the API. The consequence once that match occurs is the same, but the attack is materially harder and scores lower. To determine whether a deployment is affected, check two settings together: the static Server configuration for a non-zero services.internal-frontend.rpc.httpPort, and the effective per-namespace dynamic configuration value of component.callbacks.allowedAddresses. A deployment is exposed only when an internal frontend is deployed with a non-zero HTTP port, at least one allowlist rule admits a host, and authorization is enabled. The allowlist is empty by default, which denies all external callback URLs, and the stock static topology does not include an internal frontend. Note that a failed attach returns the error 'invalid url: url does not match any configured callback address', which proves only that one tested URL did not match and does not prove the effective allowlist is empty. To look for callbacks already attached, use DescribeWorkflowExecution, which returns callback information for a Workflow's registered completion callbacks. Operators should be aware of a gap when searching for evidence of delivery: the frontend HTTP API server records the request method and URL at debug level only, so at default log levels a delivered request is not written to the internal frontend's logs, and the absence of such log entries is not evidence that the issue was not exploited.
{
"affected": [],
"aliases": [
"CVE-2026-87858"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-21T12:17:22Z",
"severity": "HIGH"
},
"details": "Temporal Server decided whether a Workflow completion callback was internal by reading a caller-supplied HTTP header. An authenticated caller holding only write permission in a single namespace could attach a completion callback whose URL host matched the configured callback address allowlist, whose URL path was any Temporal HTTP API route, and whose header map contained a non-empty header named source. When the History service delivered that callback, the non-empty source header caused it to re-target the request at the local frontend client and rewrite only the scheme and host, preserving the caller\u0027s path, query, and request body. Where an internal frontend is deployed with its HTTP API enabled, that client resolves to the internal frontend, which authorizes every request as a system administrator without requiring authentication information. The result is that the server performs an attacker-chosen state-changing HTTP POST against its own administrative API on the caller\u0027s behalf, in namespaces where the caller has no permission. The caller never needs network access to the internal frontend, because the History service makes the request. Confirmed effects include terminating Workflows in other namespaces, registering namespaces, modifying another namespace\u0027s configuration, and deleting another namespace and its Workflows. The affected routing logic is present in both the HSM and CHASM callback delivery implementations. This description and the CVSS score in this record describe releases 1.30.0 and later, where any non-empty source header is sufficient. Releases 1.25.0 through 1.29.7 are affected by a narrower form of the same defect in which the header must exactly match a configured cluster ID, a UUID that a namespace-scoped caller cannot read through the API. The consequence once that match occurs is the same, but the attack is materially harder and scores lower. To determine whether a deployment is affected, check two settings together: the static Server configuration for a non-zero services.internal-frontend.rpc.httpPort, and the effective per-namespace dynamic configuration value of component.callbacks.allowedAddresses. A deployment is exposed only when an internal frontend is deployed with a non-zero HTTP port, at least one allowlist rule admits a host, and authorization is enabled. The allowlist is empty by default, which denies all external callback URLs, and the stock static topology does not include an internal frontend. Note that a failed attach returns the error \u0027invalid url: url does not match any configured callback address\u0027, which proves only that one tested URL did not match and does not prove the effective allowlist is empty. To look for callbacks already attached, use DescribeWorkflowExecution, which returns callback information for a Workflow\u0027s registered completion callbacks. Operators should be aware of a gap when searching for evidence of delivery: the frontend HTTP API server records the request method and URL at debug level only, so at default log levels a delivered request is not written to the internal frontend\u0027s logs, and the absence of such log entries is not evidence that the issue was not exploited.",
"id": "GHSA-vrgw-vghm-5p74",
"modified": "2026-09-21T12:32:38Z",
"published": "2026-09-21T12:32:38Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-87858"
},
{
"type": "WEB",
"url": "https://github.com/temporalio/temporal/pull/11965"
},
{
"type": "WEB",
"url": "https://github.com/temporalio/temporal/pull/11982"
},
{
"type": "WEB",
"url": "https://github.com/temporalio/temporal/pull/12085"
},
{
"type": "WEB",
"url": "https://github.com/temporalio/temporal/releases/tag/v1.30.7"
},
{
"type": "WEB",
"url": "https://github.com/temporalio/temporal/releases/tag/v1.31.3"
},
{
"type": "WEB",
"url": "https://github.com/temporalio/temporal/releases/tag/v1.32.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-VVGP-4C28-M3JM
Vulnerability from github – Published: 2026-03-03 21:52 – Updated: 2026-03-30 13:38Summary
A trusted-proxy Control UI pairing bypass accepted client.id=control-ui without device identity checks. The bypass did not require operator role, so an authenticated node role session could connect unpaired and reach node event methods.
Impact
With trusted-proxy authentication enabled, a node role websocket client could skip pairing by using client.id=control-ui. That created an authorization boundary bypass from a node-scoped connection into node event execution flows.
Affected Packages / Versions
- Package:
openclaw(npm) - Affected range:
<= 2026.2.24 - Latest published vulnerable version:
2026.2.24 - Patched in next release:
2026.2.25(pre-set below so this advisory is ready to publish after npm release)
Fix
The trusted-proxy Control UI bypass now additionally requires role === "operator".
Fix Commit(s)
ec45c317f5d0631a3d333b236da58c4749ede2a3
Release Process Note
patched_versions is intentionally pre-set to the release (2026.2.25). Advisory published with npm release 2026.2.25.2.25` is published, the remaining GHSA action is to publish this advisory.
OpenClaw thanks @tdjackey for reporting.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2026.2.24"
},
"package": {
"ecosystem": "npm",
"name": "openclaw"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2026.2.25"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-32057"
],
"database_specific": {
"cwe_ids": [
"CWE-807"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-03T21:52:16Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\nA trusted-proxy Control UI pairing bypass accepted `client.id=control-ui` without device identity checks. The bypass did not require `operator` role, so an authenticated `node` role session could connect unpaired and reach node event methods.\n\n## Impact\nWith trusted-proxy authentication enabled, a `node` role websocket client could skip pairing by using `client.id=control-ui`. That created an authorization boundary bypass from a node-scoped connection into node event execution flows.\n\n## Affected Packages / Versions\n- Package: `openclaw` (npm)\n- Affected range: `\u003c= 2026.2.24`\n- Latest published vulnerable version: `2026.2.24`\n- Patched in next release: `2026.2.25` (pre-set below so this advisory is ready to publish after npm release)\n\n## Fix\nThe trusted-proxy Control UI bypass now additionally requires `role === \"operator\"`.\n\n### Fix Commit(s)\n- `ec45c317f5d0631a3d333b236da58c4749ede2a3`\n\n## Release Process Note\n`patched_versions` is intentionally pre-set to the release (`2026.2.25`). Advisory published with npm release `2026.2.25`.2.25` is published, the remaining GHSA action is to publish this advisory.\n\nOpenClaw thanks @tdjackey for reporting.",
"id": "GHSA-vvgp-4c28-m3jm",
"modified": "2026-03-30T13:38:27Z",
"published": "2026-03-03T21:52:16Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-vvgp-4c28-m3jm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32057"
},
{
"type": "WEB",
"url": "https://github.com/openclaw/openclaw/commit/ec45c317f5d0631a3d333b236da58c4749ede2a3"
},
{
"type": "PACKAGE",
"url": "https://github.com/openclaw/openclaw"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/openclaw-authentication-bypass-via-control-ui-client-id-parameter"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "OpenClaw has a Trusted-proxy Control UI pairing bypass which allows unpaired node sessions"
}
Mitigation MIT-14
Strategy: Attack Surface Reduction
- Store state information and sensitive data on the server side only.
- Ensure that the system definitively and unambiguously keeps track of its own state and user state and has rules defined for legitimate state transitions. Do not allow any application user to affect state directly in any way other than through legitimate actions leading to state transitions.
- If information must be stored on the client, do not do so without encryption and integrity checking, or otherwise having a mechanism on the server side to catch tampering. Use a message authentication code (MAC) algorithm, such as Hash Message Authentication Code (HMAC) [REF-529]. Apply this against the state or sensitive data that has to be exposed, which can guarantee the integrity of the data - i.e., that the data has not been modified. Ensure that a strong hash function is used (CWE-328).
Mitigation MIT-4.2
Strategy: Libraries or Frameworks
- Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid.
- With a stateless protocol such as HTTP, use a framework that maintains the state for you.
- Examples include ASP.NET View State [REF-756] and the OWASP ESAPI Session Management feature [REF-45].
- Be careful of language features that provide state support, since these might be provided as a convenience to the programmer and may not be considering security.
Mitigation MIT-15
For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
Mitigation MIT-16
Strategy: Environment Hardening
When using PHP, configure the application so that it does not use register_globals. During implementation, develop the application so that it does not rely on this feature, but be wary of implementing a register_globals emulation that is subject to weaknesses such as CWE-95, CWE-621, and similar issues.
Mitigation MIT-6
Strategy: Attack Surface Reduction
- Understand all the potential areas where untrusted inputs can enter your software: parameters or arguments, cookies, anything read from the network, environment variables, reverse DNS lookups, query results, request headers, URL components, e-mail, files, filenames, databases, and any external systems that provide data to the application. Remember that such inputs may be obtained indirectly through API calls.
- Identify all inputs that are used for security decisions and determine if you can modify the design so that you do not have to rely on submitted inputs at all. For example, you may be able to keep critical information about the user's session on the server side instead of recording it within external data.
No CAPEC attack patterns related to this CWE.