Summary
Req's multipart form encoder interpolates the per-part name, filename, and content_type directly into the part headers without escaping. An attacker who can influence any of those values can inject CRLF-separated header lines, smuggle additional form fields, or prepend a whole extra part into the request the victim service sends downstream.
Details
Req.Utils.encode_form_part/2 in lib/req/utils.ex builds the per-part header iodata by concatenating the three caller-supplied strings verbatim into content-disposition: form-data; name="<name>"; filename="<filename>" and content-type: <content_type>. There is no CRLF stripping, no quote escaping, and no validation. A value containing "\r\n closes the surrounding quoted value and starts a new header line; an additional \r\n--<boundary> terminates the current part and lets the attacker prepend a smuggled part.
The flaw is reachable through every supported way of supplying a part. It is especially easy to hit when value is a %File.Stream{}, because filename then defaults to Path.basename(stream.path) and POSIX filenames may legitimately contain \r and \n. RFC 7578 / WHATWG form-data requires percent-encoding ", CR, and LF in these fields; the fix adopts that behavior.
PoC
- Construct a malicious
filename such as harmless.txt"\r\nX-Smuggled: marker\r\nContent-Disposition: form-data; name="pwned.
- Call
Req.post!(url, form_multipart: [upload: {"benign body", filename: <malicious>, content_type: "text/plain"}]).
- The emitted multipart body contains a real
X-Smuggled: header line and an extra Content-Disposition for name="pwned", alongside Req's legitimate headers.
Impact
HTTP request smuggling / multipart parameter smuggling in the HTTP client. Any application using Req to send form_multipart requests where any of name, filename, or content_type can be influenced by an untrusted source is affected, most commonly upload proxies and re-uploaders that derive filename from Path.basename/1 on a user-controlled path.
References
Summary
Req's multipart form encoder interpolates the per-part
name,filename, andcontent_typedirectly into the part headers without escaping. An attacker who can influence any of those values can inject CRLF-separated header lines, smuggle additional form fields, or prepend a whole extra part into the request the victim service sends downstream.Details
Req.Utils.encode_form_part/2inlib/req/utils.exbuilds the per-part header iodata by concatenating the three caller-supplied strings verbatim intocontent-disposition: form-data; name="<name>"; filename="<filename>"andcontent-type: <content_type>. There is no CRLF stripping, no quote escaping, and no validation. A value containing"\r\ncloses the surrounding quoted value and starts a new header line; an additional\r\n--<boundary>terminates the current part and lets the attacker prepend a smuggled part.The flaw is reachable through every supported way of supplying a part. It is especially easy to hit when
valueis a%File.Stream{}, becausefilenamethen defaults toPath.basename(stream.path)and POSIX filenames may legitimately contain\rand\n. RFC 7578 / WHATWG form-data requires percent-encoding", CR, and LF in these fields; the fix adopts that behavior.PoC
filenamesuch asharmless.txt"\r\nX-Smuggled: marker\r\nContent-Disposition: form-data; name="pwned.Req.post!(url, form_multipart: [upload: {"benign body", filename: <malicious>, content_type: "text/plain"}]).X-Smuggled:header line and an extraContent-Dispositionforname="pwned", alongside Req's legitimate headers.Impact
HTTP request smuggling / multipart parameter smuggling in the HTTP client. Any application using Req to send
form_multipartrequests where any ofname,filename, orcontent_typecan be influenced by an untrusted source is affected, most commonly upload proxies and re-uploaders that derivefilenamefromPath.basename/1on a user-controlled path.References