Unvalidated HTTP/1 request-target authority lets a client choose conn.host and conn.port in Bandit
## Summary Improper Validation of Syntactic Correctness of Input vulnerability in mtrudel bandit allows an unauthenticated remote attacker to set `conn.host` and `conn.port` to values outside the RFC 3986 host grammar via the authority of an HTTP/1 absolute-form or authority-form request target. Bandit validates the `Host` header and answers 400 when it is malformed, but when the request target carries its own authority that authority takes precedence, as RFC 9112 requires, and `Bandit.Pipeline.determine_host_and_port!/4` in `lib/bandit/pipeline.ex` passes it to the application unchecked. A request line such as `GET http://good.example@evil.example/x HTTP/1.1` with a valid `Host` header yields a `conn.host` of `good.example@evil.example`, and the same path admits NUL, DEL and non-ASCII bytes, invalid IP literals and ports outside 1..65535. The HTTP/1.0 clause needs no `Host` header at all, and `CONNECT` places the unparsed port string in `conn.port`. Applications that build absolute URLs from `conn.host`, such as through `Plug.Conn.request_url/1` or Phoenix URL helpers, emit links whose authority the attacker chose. Message framing is unaffected, and no CR or LF can reach `conn.host`. This issue affects bandit: from 0.6.0 before 1.13.0. ## Details **1. Request-target parsing** (`lib/bandit/http1/socket.ex`) `:erlang.decode_packet/3` returns an absolute-form target as `{:absoluteURI, scheme, host, port, path}` and an authority-form (`CONNECT`) target as `{:scheme, host, port}`. `resolve_request_target!/2` unwraps both into a `{scheme, host, port, path}` tuple and forwards `host` and `port` as received. `validate_request_line!/2` only rejects bytes at or below `0x20` and `0x7F`, so userinfo, delimiters, backslashes, bytes `0x80` to `0xFF`, invalid IP literals and out-of-range ports pass. For `CONNECT`, `port` is the unparsed text after the first colon. **2. Host selection** (`lib/bandit/pipeline.ex`) `determine_host_and_port!/4` has three clauses. The HTTP/1.1 clause calls `required_host_header!/1`, which parses the `Host` header with `Bandit.Headers.parse_hostlike_header!/1` and raises a 400 on a malformed value, and then discards that result whenever `target_host` is set, returning `to_string(target_host)` and `target_port` instead. The fallback clause used for HTTP/1.0 and for `CONNECT` returns the target host and port directly and never consults the `Host` header. The precedence itself follows RFC 9112 section 3.2.2; the defect is that the value which wins is the one never validated. **3. Effect** `build_conn!/6` places the pair into the `URI` from which `Plug.Conn.Adapter.conn/5` builds `conn.host` and `conn.port`. The application therefore sees host values Bandit rejects on every other route into the same field, and for `CONNECT` a binary where `Plug.Conn.port_number/0` promises an integer, which makes `Plug.Conn.request_url/1` raise. ## Proof of concept 1. Start Bandit over plaintext HTTP with a plug that returns `conn.host` and `conn.port`. 2. Open a raw TCP connection to the listener and send `GET http://good.example@evil.example/x HTTP/1.1` followed by `Host: good.example` and an empty line. The response is 200 and `conn.host` is `good.example@evil.example`. 3. Repeat with `GET http://ex\x00am.com/x HTTP/1.1` (a NUL byte in the authority) or `GET http://ex.com:99999/x HTTP/1.1`. Both are accepted, with the NUL byte in `conn.host` and 99999 in `conn.port`. 4. Send the same bytes in the `Host` header of an origin-form request instead. Bandit answers 400 for the NUL byte and the out-of-range port on 1.12.5, and for every payload once the `Host` grammar tightening on main is released. 5. Send `GET http://good.example@evil.example/x HTTP/1.0` with no `Host` header, or `CONNECT good.example@evil.example:443 HTTP/1.1`. Both are accepted, and the `CONNECT` form leaves `conn.port` as the binary `"443"`. ## Impact An unauthenticated client can make the application see any host string and any port number as the request's authority, bypassing the validation applied to the `Host` header. An application that builds absolute URLs from `conn.host`, such as password-reset or verification links, emits links whose host the attacker chose. ## Workarounds Add a plug ahead of any code that reads `conn.host` or `conn.port` that compares `conn.host` against an allow-list of the hostnames the application serves, checks that `conn.port` is an integer, and halts the request with a 400 response otherwise. Do not derive absolute URLs from `conn.host` until that check has passed.
beschreibt (1)
Zugehörige CVEs.
| ID | Schweregrad | ausnutzbar | ||
|---|---|---|---|---|
| CVE-2026-105660US | — | — | ||
Improper Validation of Syntactic Correctness of Input vulnerability in mtrudel bandit allows an unauthenticated remote attacker to set conn.host and conn.port to values outside the… | ||||
Referenzen
Externe Belege — Patches, Meldungen, Analysen.
- https://cna.erlef.org/cves/CVE-2026-105660.htmlWEB
- https://github.com/mtrudel/bandit/commit/0e3edda0edf6322e9164b827c6d3ddecd080ff2eWEB
- https://github.com/mtrudel/bandit/commit/6ea80210137b3f69caf65f67398934259dc365d0FIX
- https://github.com/mtrudel/bandit/security/advisories/GHSA-v53v-9hvg-37jfADVISORY
- https://hex.pm/packages/banditPACKAGE
Advisories wie diese benennen die exponierten Produkte, die Angreifer auf Ihrer Angriffsfläche ins Visier nehmen. Sehen Sie mit einem kostenlosen Passiv-Scan, was exponiert ist.
Kostenloser Scan