Parameter Smuggling across Golang Versions
A post by Oxeye which studies a desync attack based on Golang’s net/url
package and some subtle changes that were made to it in Go v1.17, which patched a bug where the ParseQuery()
method would consider semi-colons a valid separator. As per the RFC for the URL spec, while semi-colons are an accepted separator for the path, they aren’t for the query. Similarly, the WHATWG URL specification only lists the ampersand (&
) as a valid separator. As such, other language libraries like nodejs don’t recognize ;
as a valid separator, and so the Go maintainers fixed net/url
to conform. In go 1.17, a check was added to ParseQuery()
that would return an error if a semi-colon was present.
The problem is, the URL package’s Query()
routine that calls ParseQuery()
explicitly ignores the error return, making it silently fail. This is a perfect soup for a smuggling attack in instances where a front-end might be running a newer version of Golang and a back-end an older version. On top of that, the httputil
module’s NewSingleHostReverseProxy()
method would take a raw query string and send it as-is without parsing it. In these cases, the front-end could ignore the semi-colon separated parameter, whereas it slips through to the backend that parses it as a separate parameter. This was found in multiple open source projects, including Harbor (open source artifact registry), and HTTP reverse proxies/routers Traefik and Skipper.