VYPR
Low severityNVD Advisory· Published Jun 8, 2026· Updated Jun 8, 2026

CVE-2026-49756

CVE-2026-49756

Description

Improper Neutralization of CRLF Sequences ('CRLF Injection') vulnerability in wojtekmach Req allows multipart parameter smuggling via attacker-influenced part metadata.

Req.Utils.encode_form_part/2 in lib/req/utils.ex builds the per-part headers by interpolating the caller-supplied name, filename, and content_type values directly into the content-disposition and content-type lines with no escaping or CRLF stripping. A value containing ", \r, or \n closes the surrounding quoted value and starts a new header line; an additional \r\n-- terminates the current part and prepends a smuggled part of the attacker's choosing.

This is reachable through every supported way of supplying a part. It is particularly easy when value is a %File.Stream{}, because filename then defaults to Path.basename(stream.path) and POSIX filenames may legitimately contain \r and \n. Any application that forwards user-controlled filenames (or field names / MIME types) through Req.post/2 with form_multipart: lets an attacker inject arbitrary headers into the outgoing multipart body or smuggle additional fields and parts into the request the victim service sends downstream.

This issue affects req: from 0.5.3 before 0.6.0.

Affected products

2
  • Wojtekmach/Reqreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: >=0.5.3 <0.6.0

Patches

1
74506ff2c5ad

`encode_body`: Security fix for `:form_multipart` header injection

https://github.com/wojtekmach/reqWojtek MachJun 5, 2026via body-scan
3 files changed · +43 4
  • CHANGELOG.md+11 0 modified
    @@ -2,6 +2,17 @@
     
     ## Unreleased
     
    +  * [`encode_body`]: Security fix for `:form_multipart` header injection
    +    ([GHSA-px9f-whj3-246m](https://github.com/wojtekmach/req/security/advisories/GHSA-px9f-whj3-246m)).
    +
    +    The multipart encoder interpolated the per-part `name`, `filename`, and
    +    `content_type` into the part headers without escaping, so an
    +    attacker-controlled value could inject extra headers or smuggle additional
    +    parts into the request. These values are now escaped per RFC 7578 / WHATWG
    +    form-data (`"`, CR, and LF are percent-encoded).
    +
    +    Thanks to @PJUllrich for reporting it.
    +
     ## v0.5.18 (2026-05-20)
     
       * [`run_finch`]: Allow :finch option with IPv6 URLs.
    
  • lib/req/utils.ex+10 4 modified
    @@ -534,7 +534,7 @@ defmodule Req.Utils do
         {Stream.concat(parts1, parts2), add_sizes(size1, size2)}
       end
     
    -  defp encode_form_part({name, {value, options}}, boundary) do
    +  defp encode_form_part({name, {value, options}}, boundary) when is_atom(name) do
         options = Keyword.validate!(options, [:filename, :content_type, :size])
     
         {parts, parts_size, options} =
    @@ -574,18 +574,19 @@ defmodule Req.Utils do
     
         params =
           if filename = options[:filename] do
    -        ["; filename=\"", filename, "\""]
    +        ["; filename=\"", escape_form_param(filename), "\""]
           else
             []
           end
     
         headers =
           if content_type = options[:content_type] do
    -        ["content-type: ", content_type, @crlf]
    +        ["content-type: ", escape_form_param(content_type), @crlf]
           else
             []
           end
     
    +    name = escape_form_param(Atom.to_string(name))
         headers = ["content-disposition: form-data; name=\"#{name}\"", params, @crlf, headers]
         header = [["--", boundary, @crlf, headers, @crlf]]
     
    @@ -594,10 +595,15 @@ defmodule Req.Utils do
         |> add_form_parts({[@crlf], 2})
       end
     
    -  defp encode_form_part({name, value}, boundary) do
    +  defp encode_form_part({name, value}, boundary) when is_atom(name) do
         encode_form_part({name, {value, []}}, boundary)
       end
     
    +  # Per RFC 7578 / WHATWG form-data behavior, escape `"`, CR, and LF.
    +  defp escape_form_param(value) when is_binary(value) do
    +    URI.encode(value, &(&1 not in [?", ?\r, ?\n]))
    +  end
    +
       @doc """
       Loads .netrc file.
     
    
  • test/req/utils_test.exs+22 0 modified
    @@ -231,6 +231,28 @@ defmodule Req.UtilsTest do
                  """
         end
     
    +    test "escapes name, filename, and content_type to prevent header injection" do
    +      %{body: body} =
    +        Req.Utils.encode_form_multipart(
    +          [
    +            "na\"me\r\nX-Evil: 1":
    +              {"value", filename: ~s(ev"il\r\n--foo\r\n), content_type: "text/plain\r\nX-Evil: 2"}
    +          ],
    +          boundary: "foo"
    +        )
    +
    +      body = IO.iodata_to_binary(body)
    +
    +      assert body == """
    +             --foo\r\n\
    +             content-disposition: form-data; name=\"na%22me%0D%0AX-Evil: 1\"; filename=\"ev%22il%0D%0A--foo%0D%0A\"\r\n\
    +             content-type: text/plain%0D%0AX-Evil: 2\r\n\
    +             \r\n\
    +             value\r\n\
    +             --foo--\r\n\
    +             """
    +    end
    +
         test "it works with size" do
           %{content_type: content_type, body: body, size: size} =
             Req.Utils.encode_form_multipart([field1: {"value", size: 5}], boundary: "foo")
    

Vulnerability mechanics

Root cause

"The multipart encoder did not escape special characters in user-supplied part metadata."

Attack vector

An attacker can influence the `name`, `filename`, or `content_type` parameters when constructing a multipart form request. By including CRLF sequences or quotes within these parameters, an attacker can inject arbitrary headers or smuggle additional parts into the outgoing request [ref_id=1]. This is particularly effective when filenames are derived from user-controlled paths, as POSIX filenames can legitimately contain CRLF characters [ref_id=1].

Affected code

The vulnerability resides in the `Req.Utils.encode_form_part/2` function within `lib/req/utils.ex`. This function directly interpolates user-supplied `name`, `filename`, and `content_type` values into the multipart headers without proper sanitization or escaping [ref_id=1]. The fix is applied in the same file, introducing the `escape_form_param/1` helper function.

What the fix does

The patch modifies the `encode_form_part/2` function to escape special characters in the `name`, `filename`, and `content_type` parameters using `URI.encode/2` [patch_id=5238292]. This ensures that characters like double quotes, carriage returns, and newlines are percent-encoded, preventing them from breaking out of quoted values and injecting new headers or parts into the multipart body, adhering to RFC 7578 / WHATWG form-data specifications [ref_id=2].

Generated on Jun 8, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.