Skip to main content

CVE-2026-34715

MEDIUM
HTTP Response Splitting (CWE-113)
2026-04-01 https://github.com/vshakitskiy/ewe GHSA-x2w3-23jr-hrpf
5.3
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
5.3 MEDIUM
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N

Primary rating from GitHub Advisory · only source for this CVE.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
Low
Availability
None

Lifecycle Timeline

2
Analysis Generated
Apr 01, 2026 - 23:16 vuln.today
CVE Published
Apr 01, 2026 - 22:18 nvd
MEDIUM 5.3

DescriptionGitHub Advisory

Summary

The encode_headers function in src/ewe/internal/encoder.gleam directly interpolates response header keys and values into raw HTTP bytes without validating or stripping CRLF (\r\n) sequences. An application that passes user-controlled data into response headers (e.g., setting a Location redirect header from a request parameter) allows an attacker to inject arbitrary HTTP response content, leading to response splitting, cache poisoning, and possible cross-site scripting.

Notably, ewe *does* validate CRLF in incoming request headers via validate_field_value() in the HTTP/1.1 parser - but provides no equivalent protection for outgoing response headers in the encoder.

Details

File: src/ewe/internal/encoder.gleam

Vulnerable code:

gleam
fn encode_headers(headers: List(#(String, String))) -> BitArray {
  let headers =
    list.fold(headers, <<>>, fn(acc, headers) {
      let #(key, value) = headers
      <<acc:bits, key:utf8, ": ", value:utf8, "\r\n">>
    })

  <<headers:bits, "\r\n">>
}

Both key and value are embedded directly into the BitArray output. If either contains \r\n, the resulting bytes become a structurally valid but attacker-controlled HTTP response, terminating the current header early and injecting new headers or a second HTTP response.

Contrast with request parsing (src/ewe/internal/http1.gleam): incoming header values are protected:

gleam
use value <- try(
  validate_field_value(value) |> replace_error(InvalidHeaders)
)

No analogous validation exists for outgoing header values in the encoder. The solution is to strip or reject \r (0x0D) and \n (0x0A) from all header key and value strings in encode_headers before encoding, mirroring the validation already applied to incoming request headers via validate_field_value()

PoC

An ewe application echoes a user-supplied redirect URL into a Location header:

gleam
fn handle_request(req: Request) -> Response {
  let redirect_url =
    request.get_query(req)
    |> result.try(list.key_find(_, "next"))
    |> result.unwrap("/home")

  response.new(302)
  |> response.set_header("location", redirect_url)
  |> response.set_body(ewe.Empty)
}

Attacker request:

bash
printf 'GET /?next=https://example.com%%0d%%0aX-Injected:%%20true HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc -w 2 localhost 8080

Resulting response:

HTTP/1.1 302 Found
location: https://example.com
X-Injected: true
content-length: 0
date: Tue, 24 Mar 2026 07:53:00 GMT
connection: keep-alive

The X-Injected: true header appears as a separate response header, confirming that CRLF sequences in user input are not sanitized by the encoder.

AnalysisAI

HTTP response splitting in ewe's encode_headers function allows remote attackers to inject arbitrary HTTP response headers and content by embedding CRLF sequences in user-controlled response header values, enabling cache poisoning and cross-site scripting attacks. The vulnerability affects ewe versions that do not validate outgoing response header keys and values, despite implementing equivalent validation for incoming request headers. A proof-of-concept demonstrates injection of custom headers through a redirect URL parameter passed directly to the Location header without sanitization.

Technical ContextAI

The ewe web framework (Erlang package) contains an HTTP/1.1 encoder in src/ewe/internal/encoder.gleam that constructs response headers by directly interpolating header keys and values into raw HTTP byte sequences (BitArray) without stripping or validating CRLF characters (\r\n, bytes 0x0D and 0x0A). The root cause is CWE-113 (Improper Neutralization of CRLF Sequences in HTTP Headers), a classic HTTP injection vulnerability. The encode_headers function concatenates user-supplied strings into the BitArray format using UTF-8 encoding without any character validation: <<acc:bits, key:utf8, ": ", value:utf8, "\r\n">>. This directly mirrors the structure of HTTP/1.1 header syntax, so if an attacker controls a header value and embeds \r\n, they can prematurely terminate the current header line and inject additional headers or even a complete second HTTP response. Notably, ewe's HTTP/1.1 request parser (src/ewe/internal/http1.gleam) implements validate_field_value() to sanitize incoming request headers, creating an asymmetric validation posture where inbound requests are protected but outbound responses are not.

RemediationAI

Apply the vendor-released patch by upgrading ewe to the patched version once released by the ewe maintainers; consult the GitHub advisory at https://github.com/vshakitskiy/ewe/security/advisories/GHSA-x2w3-23jr-hrpf for the specific patched version number and release date. The primary fix is to add CRLF validation to the encode_headers function in src/ewe/internal/encoder.gleam, mirroring the existing validate_field_value() function used for incoming request headers. Specifically, header keys and values must be stripped of or rejected if they contain \r (0x0D) or \n (0x0A) bytes before being embedded into the BitArray output. As a temporary mitigation prior to patching, applications should implement application-level validation: sanitize all user-supplied data before passing it to response.set_header() by removing or rejecting any string containing CRLF sequences, and prefer static or framework-controlled header values over user-supplied ones. Developers should also audit all response header assignments in their ewe applications to identify code paths that accept user input.

Share

CVE-2026-34715 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy