layers/geneve: seven zero bytes panic the decoder — two off-by-one length guards #8
Labels
No labels
core
cpu-dos
critical
dos
evasion
has-poc
high
integer-overflow
ip4defrag
layers
low
medium
memory-exhaustion
other
panic
pcapgo
pentest-2026-08
rce
tcpassembly
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
noi/gopacket#8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: high ·
layers/geneve.go:96andlayers/geneve.go:55Geneve.DecodeFromBytesand its option helper both validate a length one byte short of what they then index. Two distinct off-by-ones, both reachable from a single UDP datagram to port 6081.Defect 1 — the outer guard is short by one
With
OptionsLength == 0the guard reduces tolen(data) < 7, so a 7-byte payload is accepted. The loop does not run, anddata[:offset]slices a 7-byte buffer at 8.Defect 2 — the option guard is short by one
An option header is 4 bytes. The guard admits 3.
Reproduction
Seven zero bytes. On the wire that is a 49-byte frame: Ethernet + IPv4 + UDP + 7 bytes of payload to port 6081.
Breaches the second defect:data[0]&0x3f == 1declares one 4-byte option, the outer guard demandslen >= 11, and the loop then handsdecodeGeneveOptiona 3-byte slice.Through a full frame:
Reachability
Geneveis registered onUDP 6081(layers/iana_ports.go/ the UDP port map), so any packet a rival sends to that port is decoded. No handshake, no host has to be listening — the capturer parses it regardless.Honest scoping:
layers.Genevedoes not implementgopacket.DecodingLayer(noCanDecode), so it cannot be reached throughDecodingLayerParser. Undergopacket.NewPacketwith default options the panic is recovered and surfaces as an error layer. It is a hard crash for any caller that setsDecodeOptions{SkipDecodeRecovery: true}— a common choice in throughput-sensitive analysers, and the option the shippedlayers/fuzz_layer.goharness itself exercises.Even when recovered, the cost is not nothing:
recover()plus stack unwinding per packet is orders of magnitude more expensive than a decode, so a stream of 49-byte frames is a cheap throughput attack, and the packet is dropped from analysis entirely.Fix
Both belong under a table test of short inputs —
for n := 0; n < 16; n++over truncations of a valid Geneve packet, asserting an error rather than a panic. See also #9, a third defect in the same function.Verified against
b7d9dbdon Go 1.24.4. PoCs:geneve,geneve2.