GitHub Advisoryadvisory · Go
GHSA-6r28-9ppf-4hj5 GoPacket's Diameter AVP decoder: uint32 underflow on vendor header size leads to unbounded ~4 GiB allocation (unauthenticated remote DoS)
MEDIUM
Official page ↗
- Aliases
- CVE-2026-54345
- Ecosystem
- Go
- Packages
- github.com/gopacket/gopacket
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
- Weaknesses
- CWE-191, CWE-770
- Reviewed
- 2026-07-28
Affected packages
• Go github.com/gopacket/gopacket: < 1.6.1 → fixed in 1.6.1
Description
Summary
The Diameter AVP decoder in `github.com/gopacket/gopacket` computes `dataLength := avp.Length - uint32(headerSize)` without first ensuring `avp.Length >= headerSize`. When the Vendor flag is set, `headerSize` is 12, but the only length guard upstream rejects `avp.Length CWE-770 unbounded allocation).
Root cause (file:line @ v1.6.0)
`layers/diameter_avp_decoders.go`, `decodeDiameterAVP`:
```go
avp.Length = uint32(data[5])= 8 was checked
dataOffset = 12
}
paddedLength := avp.Length // equals avp.Length; for avp.Length panic
```
For `avp.Length` in `{8, 9, 10, 11}` with the Vendor flag set: the `avp.Length sink)
`LayerTypeDiameter` is a registered decoder (`layertypes.go:159`, `RegisterLayerType(154, ... decodeDiameter)`):
`decodeDiameter` -> `(*Diameter).DecodeFromBytes` (diameter.go:118) -> parses the 20-byte header -> `avpData := data[20:d.MessageLength]` -> AVP loop `decodeDiameterAVP(avpData)` (diameter.go:158) -> sink at `diameter_avp_decoders.go:57`.
Diameter (RFC 6733) is a TCP/SCTP base protocol used for AAA and telecom/5G signaling. Any service that parses Diameter with gopacket (packet collectors, signaling monitors, IDS/analysis tooling) processes attacker-sent or attacker-forwarded Diameter messages with no authentication involved, so any host able to deliver such a message to the parser reaches the sink. The same path is reached via `gopacket.NewPacket(data, LayerTypeDiameter, ...)`. The Diameter layer is specific to this gopacket fork (the original google/gopacket has no Diameter layer), so there is no upstream sibling fix.
Impact
Unauthenticated remote denial of service via memory exhaustion. Each malicious 32-byte Diameter message requests a ~4 GiB allocation (amplification ~1.34e8x over the input). There is no memory corruption and no code execution -- the impact is resource exhaustion / process termination. Severity assessed as Medium (unauthenticated remote DoS, no memory-safety violation).
Note on consumer behavior (measured end-to-end against a deployed collector, see PoC):
• A collector using the recovering `gopacket.NewPacket(..., gopacket.Default)` API survives a *single* malicious message: the ~4 GiB `make([]byte, dataLength)` runs (in-process `runtime.MemStats` shows a 4096 MB `TotalAlloc` delta per message), but the immediately-following out-of-bounds `copy` panics before the allocator faults in the 4 GiB of physical pages, the panic is recovered into an `ErrorLayer`, and the reservation is reclaimed by the GC. RSS therefore does not commit on a single message.
• Two malicious messages in succession reliably OOM-kill the collector under a 256 MB cap: the second `make` commits physical pages before the first reservation is fully returned to the cgroup, and the kernel cgroup OOM-killer terminates the process (`OOMKilled=true`, exit 137). This was reproduced with two messages sent strictly serially to the single-threaded accept loop (no concurrency required).
• Consumers that call `DecodeFromBytes` directly (common in performance-sensitive collectors) or set `SkipDecodeRecovery: true` additionally get an uncaught panic / crash on the first message.
In all cases the underlying defect is the same unbounded ~4 GiB allocation driven by an attacker-controlled field; the only variable is how many messages it takes to exhaust a given memory limit.
Proof of Concept
This PoC is an end-to-end test against a real deployed Diameter collector. A minimal but realistic TCP collector (built on the public gopacket API) runs inside a hard-capped 256 MB container; an independent client process sends real malicious Diameter messages over a real TCP socket; the collector process is then observed to die. A benign message is used as a negative control. The harness pins `github.com/gopacket/[email protected]` (the sink is confirmed at the v1.6.0 tag, `layers/diameter_avp_decoders.go:56-58`).
Collector (real TCP Diameter collector)
```go
// collector.go — accepts a TCP connection, reads one Diameter message (framed by
// the 24-bit Message Length in the base header), builds a gopacket.Packet rooted
// at LayerTypeDiameter and accesses the layer, which drives the registered
// Diameter decoder over the attacker-controlled bytes.
package main
import (
"fmt"
"io"
"net"
"os"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
)
func readDiameterMessage(conn net.Conn) ([]byte, error) {
hdr := make([]byte, 20)
if _, err := io.ReadFull(conn, hdr); err != nil {
return nil, err
}
msgLen := uint32(hdr[1])= 12` so the AVP carries `avpLen-12` real bytes and parses cleanly.
```go
// client.go — usage: client <…
Related
Alias · CVE (1)
- CVE-2026-54345 gopacket provides packet processing capabilities for Go. vulnerability