{"id":"GHSA-p8x9-v4hv-2w4g","summary":"Rack: Multipart boundary ambiguity guard in Rack::Multipart::Parser can be bypassed with a quoted boundary value, allowing WAF bypass","details":"## Summary\n\nThe guard added for CVE-2026-26961 (GHSA-vgpv-f759-9wx3), which rejects multipart requests with more than one `boundary` parameter, only inspects the part of the `Content-Type` header after the boundary it extracts. A `Content-Type` whose first `boundary` parameter is empty or begins with a quote, semicolon, or comma is skipped by the extraction pattern, so a second `boundary` parameter is accepted silently and Rack parses the body with a boundary that a conforming parser would not derive from the header.\n\n## Details\n\n`Rack::Multipart::Parser.parse_boundary` extracts the boundary with a regular expression that uses a lazy prefix and a value class excluding `\"`, `;`, and `,`. For a header such as `multipart/form-data; boundary=\";boundary=AaB03x\"` or `multipart/form-data; boundary=; boundary=AaB03x`, the pattern skips the first `boundary=` and anchors on the second, and the subsequent check for another `boundary=` in the remaining text finds nothing. Rack then uses `AaB03x`.\n\nRFC 9110 parses the first header as a single parameter whose value is `;boundary=AaB03x`, which is not a legal boundary under RFC 2046, so the only conformant outcome is rejection. Inspecting intermediaries that implement the standard grammar, such as ModSecurity, see a different boundary or no multipart body at all.\n\nThe fix counts `boundary=` occurrences across the entire header value and rejects the request when there is more than one.\n\n## Impact\n\nAs with CVE-2026-26961, an attacker can craft a multipart request that a web application firewall or other inspecting proxy in front of a Rack application interprets differently from Rack, so form fields and file uploads reach the application without inspection. Rack itself grants no capability beyond that of a well-formed request with the same body. All supported versions, including 2.2, are affected.\n\n## Mitigation\n\n* Update to a patched version of Rack.\n* Reject requests whose `Content-Type` contains more than one `boundary` parameter at the reverse proxy.","aliases":["CVE-2026-125108"],"modified":"2026-11-10T19:45:09.723995662Z","published":"2026-11-10T19:26:28Z","database_specific":{"cwe_ids":["CWE-436"],"severity":"LOW","github_reviewed":true,"github_reviewed_at":"2026-11-10T19:26:28Z","nvd_published_at":"2026-11-09T22:18:41Z"},"references":[{"type":"WEB","url":"https://github.com/rack/rack/security/advisories/GHSA-p8x9-v4hv-2w4g"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-125108"},{"type":"WEB","url":"https://github.com/rack/rack/commit/19a1a19cb4da97d61c8d5effeb2e927c44370ae9"},{"type":"WEB","url":"https://github.com/rack/rack/commit/a29512324265790a5536b830490075d0d79323be"},{"type":"WEB","url":"https://github.com/rack/rack/commit/d5e928b803da30b8e40171dbd4be39bed7daa6fa"},{"type":"PACKAGE","url":"https://github.com/rack/rack"},{"type":"WEB","url":"https://github.com/rack/rack/releases/tag/v2.2.25"},{"type":"WEB","url":"https://github.com/rack/rack/releases/tag/v3.1.23"},{"type":"WEB","url":"https://github.com/rack/rack/releases/tag/v3.2.8"},{"type":"WEB","url":"https://github.com/rubysec/ruby-advisory-db/blob/master/gems/rack/GHSA-p8x9-v4hv-2w4g.yml"}],"affected":[{"package":{"name":"rack","ecosystem":"RubyGems","purl":"pkg:gem/rack"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.2.25"}]}],"versions":["0.1.0","0.2.0","0.3.0","0.4.0","0.9.0","0.9.1","1.0.0","1.0.1","1.1.0","1.1.1","1.1.1.pre","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.2.0","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","1.2.6","1.2.7","1.2.8","1.3.0","1.3.0.beta","1.3.0.beta2","1.3.1","1.3.10","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.3.8","1.3.9","1.4.0","1.4.1","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.5.0","1.5.0.beta.1","1.5.0.beta.2","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.6.0","1.6.0.beta","1.6.0.beta2","1.6.1","1.6.10","1.6.11","1.6.12","1.6.13","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","2.0.0.alpha","2.0.0.rc1","2.0.1","2.0.2","2.0.3","2.0.4","2.0.5","2.0.6","2.0.7","2.0.8","2.0.9","2.0.9.1","2.0.9.2","2.0.9.3","2.0.9.4","2.1.0","2.1.1","2.1.2","2.1.3","2.1.4","2.1.4.1","2.1.4.2","2.1.4.3","2.1.4.4","2.2.0","2.2.1","2.2.10","2.2.11","2.2.12","2.2.13","2.2.14","2.2.15","2.2.16","2.2.17","2.2.18","2.2.19","2.2.2","2.2.20","2.2.21","2.2.22","2.2.23","2.2.24","2.2.3","2.2.3.1","2.2.4","2.2.5","2.2.6","2.2.6.1","2.2.6.2","2.2.6.3","2.2.6.4","2.2.7","2.2.8","2.2.8.1","2.2.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-p8x9-v4hv-2w4g/GHSA-p8x9-v4hv-2w4g.json"}},{"package":{"name":"rack","ecosystem":"RubyGems","purl":"pkg:gem/rack"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.0.0"},{"fixed":"3.1.23"}]}],"versions":["3.0.0","3.0.1","3.0.10","3.0.11","3.0.12","3.0.13","3.0.14","3.0.15","3.0.16","3.0.17","3.0.18","3.0.2","3.0.3","3.0.4","3.0.4.1","3.0.4.2","3.0.5","3.0.6","3.0.6.1","3.0.7","3.0.8","3.0.9","3.0.9.1","3.1.0","3.1.1","3.1.10","3.1.11","3.1.12","3.1.13","3.1.14","3.1.15","3.1.16","3.1.17","3.1.18","3.1.19","3.1.2","3.1.20","3.1.21","3.1.22","3.1.3","3.1.4","3.1.5","3.1.6","3.1.7","3.1.8","3.1.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-p8x9-v4hv-2w4g/GHSA-p8x9-v4hv-2w4g.json"}},{"package":{"name":"rack","ecosystem":"RubyGems","purl":"pkg:gem/rack"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0"},{"fixed":"3.2.8"}]}],"versions":["3.2.0","3.2.1","3.2.2","3.2.3","3.2.4","3.2.5","3.2.6","3.2.7"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-p8x9-v4hv-2w4g/GHSA-p8x9-v4hv-2w4g.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N"}]}