Skip to content

net/http: ServerMux does not sanitize ".%2e" ("..") from url path #70130

Closed as not planned
@WestleyK

Description

@WestleyK

Go version

go1.22.6 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/westley/.cache/go-build'
GOENV='/home/westley/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/westley/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/westley/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.6'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='<redacted>/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1640283443=/tmp/go-build -gno-record-gcc-switches'

What did you do?

The http server mux implies it does request path sanitization, and it does work with .. in the url path, however, it does not work with .%2e.

Start a basic http server, go run main.go

Basic http server code
package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/example/{paths...}", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("URL:", r.URL.Path)
		fmt.Println("Paths:", r.PathValue("paths"))
	})

	server := http.Server{
		Handler: mux,
		Addr:    ":8000",
	}

	log.Fatal(server.ListenAndServe())
}

What did you see happen?

The http server mux does not sanitize a path with url encoded ..

Tested with .. (expected)

$ curl -v --path-as-is localhost:8000/example/foo/../../../etc/passwd
* Uses proxy env variable no_proxy == 'localhost,127.0.0.0/8,::1'
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /example/foo/../../../etc/passwd HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html; charset=utf-8
< Location: /etc/passwd
< Date: Wed, 30 Oct 2024 21:19:39 GMT
< Content-Length: 46
< 
<a href="/etc/passwd">Moved Permanently</a>.

* Connection #0 to host localhost left intact

Tested with .%2e (possible security issue, does not get sanitized)

$ curl -v --path-as-is localhost:8000/example/foo/.%2e/.%2e/.%2e/etc/passwd
* Uses proxy env variable no_proxy == 'localhost,127.0.0.0/8,::1'
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /example/foo/.%2e/.%2e/.%2e/etc/passwd HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 30 Oct 2024 21:19:54 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

Output from the server:

URL: /example/foo/../../../etc/passwd
Paths: foo/../../../etc/passwd

What did you expect to see?

I expected http server mux to sanitize the url encoded ., and reject the request before it gets to the handler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions