Parameter Smuggling across Golang Versions

We discussed this vulnerability during Episode 151 on 19 September 2022

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.