CVE-2024-24786 Go Protobuf DoS
A few months ago, I found and reported a CVE in Go's google.golang.org/protobuf module, versions < 1.33.0.
The nvd.nist.gov National Vulnerability Database has an entry for my CVE:
The
protojson.Unmarshal
function can enter an infinite loop when unmarshaling certain forms of invalid JSON. This condition can occur when unmarshaling into a message which contains agoogle.protobuf.Any
value, or when theUnmarshalOptions.DiscardUnknown
option is set.
I fuzz Go packages from time to time using Go’s first-party fuzzing support, which is how I found the issue. Below is an shortened version of the fuzz test I used.
package demo
import (
"testing"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/anypb"
)
func FuzzProtojsonUnmarshal(f *testing.F) {
f.Fuzz(func(t *testing.T, b []byte) {
var pb anypb.Any
_ = protojson.Unmarshal(b, &pb)
})
}
You can run it with this command.
go test . -fuzz=FuzzProtojsonUnmarshal
At a high level, the test repeats these steps:
- Generate random bytes.
- Try to convert the bytes from JSON to a message of type
Any
. - Fail if the conversion panics or hangs.
After a few minutes, the fuzz test fails and prints the error message "fuzzing process hung or terminated unexpectedly".
The demo is non-deterministic, but my original run failed on the input {"":}
— an invalid dictionary with a missing value.
More details about the vulnerability are available in Go changelist #569356, which fixed the CVE. The CL adds another case to a switch
statement to detect the issue, but unfortunately, the switch still isn't exhaustive. (If you want to require exhaustive switch
statements, golangci-lint
has an exhaustive
linter. The linter's also useful when hunting for bugs.)
In practice, I don’t think this CVE is a serious issue, since bad inputs only cause a program to hang, not panic. Regardless, I'm surprised this vulnerability wasn’t found earlier, given how easy the fuzz test is to write. Perhaps it was missed because the protojson
package implements its own custom JSON package instead of importing encoding/json
from the Go standard library, which receives much more scrutiny.
Anyway, if you write Go code or hunt for Go bugs, I recommend writing fuzz tests. Functions with names including Unmarshal
, Parse
, or Decode
are good candidates to start with, especially if they take untrusted input.
Timeline:
- 2024-01-27: I found the vulnerability by fuzzing.
- 2024-01-28: I reported the vulnerability to Google.
- 2024-01-29: Google acknowledged my report.
- 2024-02-20: Google issued me a $500 reward.
- 2024-03-05: Google announced CVE-2024-24786 and released a fix.