Go: net/http: ListenAndServe is vulnerable to DNS rebinding attacks

Created on 21 Feb 2018  路  5Comments  路  Source: golang/go

What version of Go are you using (go version)?

go version go1.10 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOCACHE="/home/rob/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/rob/go/gopath"
GORACE=""
GOROOT="/usr/lib/go"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build916972665=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I wrote this small sample http listener code:

package main

import "net/http"

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))
    })
    http.ListenAndServe("localhost:8080", nil)
}

This, as expected, only listens for loopback connections, and it does not respond to requests coming from outside the machine.

I then tried, from the local host, to perform a request with an arbitrary Host header:

 curl localhost:8080 --verbose --header "Host: victim.com"
* Rebuilt URL to: localhost:8080/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: victim.com
> User-Agent: curl/7.58.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Wed, 21 Feb 2018 10:17:55 GMT
< Content-Length: 11
< Content-Type: text/plain; charset=utf-8
< 
* Connection #0 to host localhost left intact
Hello World%       

What did you expect to see?

Either the request to fail or the documentation for ListenAndServe or Handler to say that the local listener does not check for the Host header and is thus vulnerable to DNS rebinding attacks.

What did you see instead?

The request obtaining a response, without checks on the host header, and without any documentation on this issue.

Documentation FrozenDueToAge Security help wanted

Most helpful comment

I suppose we could add some docs about the Host header somewhere.

All 5 comments

Thank you for reporting this @empijei!

I'll page some of the folks on the net/http and security crews @bradfitz @agl @tombergan @FiloSottile.

Depending on the confirmation and severity of the issue after we evaluate it, perhaps in the future, please report security issues first to [email protected] as detailed at https://golang.org/security, but either way, thank you for reporting it!

Check the documentation for ServeMux

In order to only serve your handle for a specific host, your pattern should read:

http.HandleFunc("example.com/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World"))
})

Then a request for victim.com will return a 404.

Oh, I didn't know that, thanks.
Do you think it should be mentioned somewhere else? Or maybe this issue should be described in the documentation?

I suppose we could add some docs about the Host header somewhere.

Change https://golang.org/cl/115116 mentions this issue: net/http: document that Handlers are resposible for validating Host headers

Was this page helpful?
0 / 5 - 0 ratings