Threats tagged 'brew-mikutter-cve-2026-126354'
1 threat
Most recently published first
| Severity | CVSS | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|
| Medium | 4.8 | CVE-2026-126354Mikutter · Addressable URI normalization resolves some dotted path segments to the wrong path Medium4.8GCVE DatabaseCVE-2026-126354 ### Summary `Addressable::URI.normalize_path` resolves some paths containing `..` segments to a different path than RFC 3986 does. When a path climbs above the root before any other `..` is resolved, the segments that later `..` segments should remove are instead kept: `/../../public/x/../../secret` normalizes to `/public/x/secret` instead of `/secret`. `URI#normalize`, `URI#join`, `URI#==` and `URI#===` all use this normalization. An application that checks a normalized Addressable path against an allowlist, but sends the original URI to something that resolves it according to RFC 3986, can be made to allow a request for a path outside the allowlist. ### Details `normalize_path` in 2.9.0 (`lib/addressable/uri.rb`) removes dot segments by repeating a set of rewrites until nothing changes. On each pass it finds the first `/segment/../` pair with `RULE_2B_2C`, then replaces every textual copy of that pair in the path with `/`: ```ruby pair = normalized_path.match(RULE_2B_2C) ... regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|" regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)" ... mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH) ``` When the first pair found is `/../../`, where the "segment" is itself `..`, every `/../../` in the path is replaced with `/`. That includes copies later in the path, where each `..` should have removed the segment before it. For `/../../public/x/../../secret`: - RFC 3986 §5.2.4 drops the two leading `..` segments, which can't go above the root, then removes `x` and `public`, giving `/secret`. - 2.9.0 replaces both copies of `/../../` with `/` in one step, giving `/public/x/secret`. Paths where an ordinary `/segment/../` pair comes first, such as `/a/../../b/c/../../d`, are resolved correctly. This was introduced in 2.2.5. Versions 2.0.0 through 2.2.4 and earlier versions resolve these paths correctly. It is fixed in 2.9.1, which replaces the rewrite loop with a single pass that correctly follows RFC 3986 §5.2.4. ### Proof of concept ```ruby require "addressable/uri" require "uri" url = "http://example.com/../../public/x/../../secret" # Addressable's normalized path, compared with RFC 3986 resolution by Ruby's URI library puts Addressable::URI.parse(url).normalize.path puts URI.join("http://example.com/", "../../public/x/../../secret").path # join and == use the same normalization puts Addressable::URI.join("http://example.com/", "../../public/x/../../secret").path puts Addressable::URI.parse(url) == Addressable::URI.parse("http://example.com/public/x/secret") # An allowlist check on the normalized path passes, but the request reaches /secret def allowed?(url) = Addressable::URI.parse(url).normalize.path.start_with?("/public/") puts allowed?(url) ``` Output with Addressable 2.9.0: ``` /public/x/secret /secret /public/x/secret true true ``` Output with Addressable 2.9.1: ``` /secret /secret /secret false false ``` ### Impact This is an interpretation conflict (CWE-436) between Addressable and other RFC 3986 resolvers, which could lead to path traversal past an allowlist (CWE-22). An application is affected when it does both of these: - makes an access decision on a path normalized or joined by Addressable, such as "only paths under `/public/` may be proxied" or "only these redirect targets are allowed"; - sends the original, unnormalized URI to a component that resolves dot segments itself, such as an HTTP client, a proxy, a web server or a browser. An attacker may be able to bypass a check by exploiting behavioral differences between URI parsers. Impact from this is application-dependent. Applications that use Addressable's normalized form for both the check and the request are unaffected, as are applications that reject paths containing `..` segments. As a work-around for unpatched versions, before making access decisions on a path, either reject any containing `..` segments, including percent-encoded forms such as `%2e%2e`, or use the same normalized value for both the check and the request. | GCVE Database |
Page 1 of 1