GHSA-358w-w75h-x6rx

Suggest an improvement
Source
https://github.com/advisories/GHSA-358w-w75h-x6rx
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-358w-w75h-x6rx/GHSA-358w-w75h-x6rx.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-358w-w75h-x6rx
Aliases
Published
2026-11-10T15:56:49Z
Modified
2026-11-10T16:15:04Z
Severity
  • 5.9 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
gopacket: SCTP chunk sub-decoder out-of-bounds panics on crafted segments
Details

Summary

gopacket decodes SCTP (IP protocol 132) by walking the chunk chain through per-chunk-type decoder functions (decodeSCTPInit, decodeSCTPShutdown, decodeSCTPError, decodeSCTPSack, in layers/sctp.go). Each of these reads fixed-size header fields and runs count-driven loops without validating the chunk's declared length against the minimums those reads require. A single crafted SCTP segment causes a runtime panic (out-of-bounds slice read, or inverted slice from unsigned length arithmetic) on any decode path that does not install gopacket's per-packet panic recovery (gopacket.DecodeOptions.SkipDecodeRecovery: true); with the default API the same input degrades to a per-packet decode error.

The defect is present in every released version through v1.7.1 and at current master (18c5805). It is the SCTP component named - but not remediated - in CVE-2026-65819 (GHSA-8mcr-459q-5mx2, "Multiple layer decoders panic …", patched per advisory metadata in v1.7.1). The v1.7.1 hardening commit b772f56 ("layers:various: add protocol decoder length checks (#163)") added generic bounds to the chunk dispatcher and decodeSCTPChunk, but left every chunk sub-decoder without a per-type minimum-length check. The advisory's own description lists "SCTP chunk/parameter decoders (sctp.go)" among items "being confirmed; they should be remediated together with the above" - this report demonstrates that, at v1.7.1 and HEAD, that remediation still does not exist.

Patches

Fixed in v1.7.2 by commit 5080443 ("layers/sctp: bound chunk sub-decoders and parameters"). INIT/INIT-ACK, SACK and SHUTDOWN now check their chunk type's minimum length, SACK rejects gap-ack and duplicate-TSN counts the chunk can't hold, and decodeSCTPParameter rejects TLV lengths below 4 or beyond the buffer. Each case returns a decode error instead of panicking. The same commit fixes a panic on valid input, where a final parameter's padding lies outside the chunk length (RFC 9260 3.2). Regression tests: TestDecodeSCTPChunkRegression and TestDecodeSCTPParameterPadding in layers/decode_oob_test.go.

Upgrade with go get github.com/gopacket/gopacket@v1.7.2.

Workarounds

Before v1.7.2, keep panic recovery on: use gopacket.NewPacket without SkipDecodeRecovery, and DecodingLayerParser with IgnorePanic: false. The crafted packet then fails to decode instead of crashing the process.

Vulnerability Details

All line numbers refer to layers/sctp.go at commit 18c5805028329a6a8944f054d8d3f64ebc0f1373 (master, 2026-09-01; v1.7.1-1-g18c5805).

The shared entry point already present in v1.7.1 validates only that a chunk is present and that its declared length is ≥ 4 and covered by real bytes:

// sctp.go:129
func decodeSCTPChunk(data []byte) (SCTPChunk, error) {
	if len(data) < 4 { ... }
	length := binary.BigEndian.Uint16(data[2:4])
	if length < 4 { ... }
	actual := roundUpToNearest4(int(length))
	ct := SCTPChunkType(data[0])
	if ct == SCTPChunkTypeData && length < 16 { ... }   // only DATA gets a 16-byte minimum
	if len(data) < actual { ... }
	...

The Data-chunk minimum of 16 bytes is never applied to the other chunk types, and none of the sub-decoders re-check before indexing. Four independent panic paths result (each reproduced at HEAD):

Defect 1 - INIT / INIT-ACK chunks (decodeSCTPInit, sctp.go:423): reads data[4:8], data[8:12] (line 431), data[12:14], data[14:16], data[16:20] (line 434), then slices paramData := data[20:sc.ActualLength] (line 436).

  • Declared length 8–11, actual 8–12 -> data[8:12] reads past the chunk: panic: runtime error: slice bounds out of range [:12] with capacity 8
  • Declared length 15, actual 16 -> data[16:20] past the chunk: panic: runtime error: slice bounds out of range [:20] with capacity 16
  • Declared length ≤ 19 -> the data[20:ActualLength] slice is inverted (panic) or the subsequent parameter loop runs on attacker data without the decodeSCTPParameter length check (Defect 3).

Defect 2 - SHUTDOWN chunks (decodeSCTPShutdown, sctp.go:670): reads binary.BigEndian.Uint32(data[4:8]) (line 677) on a chunk whose declared length is only validated as ≥ 4: panic: runtime error: slice bounds out of range [:8] with capacity 4 (4-byte chunk).

Defect 3 - ERROR (and HEARTBEAT/INIT) parameter decoder (decodeSCTPParameter, sctp.go:193): the crafted Length field is used to slice with no bound against the parameter buffer:

// sctp.go:193
func decodeSCTPParameter(data []byte) SCTPParameter {
	length := binary.BigEndian.Uint16(data[2:4])
	return SCTPParameter{
		...
		Value:        data[4:length],   // line 198: no check length >= 4 or length <= len(data)
		ActualLength: roundUpToNearest4(int(length)),
	}
}
  • length < 4 -> inverted slice: panic: runtime error: slice bounds out of range [4:0] (reached from decodeSCTPError line 632 paramData := data[4:sc.Length], and from the INIT (438) / HEARTBEAT (578) parameter loops).
  • length > len(data) -> slice-bounds-check panic (Go's slice bounds check panics; the read never silently over-reads).

Defect 4 - SACK chunk count loops (decodeSCTPSack, sctp.go:484): NumGapACKs / NumDuplicateTSNs (attacker-controlled uint16 fields) drive loops that slice the remaining bytes with no bound:

// sctp.go:512
bytesRemaining := data[16:]
for i := 0; i < int(sc.NumGapACKs); i++ {                       // line 513
	sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2]))  // line 514
	bytesRemaining = bytesRemaining[2:]
}
for i := 0; i < int(sc.NumDuplicateTSNs); i++ {                 // line 517
	sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4]))  // line 518
	bytesRemaining = bytesRemaining[4:]
}

A 16-byte SACK chunk declaring NumGapACKs = 1 with no following bytes: panic: runtime error: slice bounds out of range [:2] with capacity 0

Note this is not the sFlow/Diameter "unbounded allocation" family already covered by CVE-2026-54332/CVE-2026-54345; the SACK make() capacities are already capped against ActualLength (lines 505–510) - the loop iteration counts are not, so the panic is an out-of-bounds read, not an allocation.

History / version range. decodeSCTPInit and the chunk sub-decoders were introduced in commit 2fca351 (original full-featured SCTP implementation; at the time the file was named layer_sctp.go - the current sctp.go path dates from the rename in dea6b39). The sinks are present in every release from v1.0.0 through v1.7.1 and at HEAD (also confirmed at v1.6.0). b772f56 (2026-07-08, included in v1.7.1) touched layers/sctp.go but only hardened the dispatcher (decodeWithSCTPChunkTypePrefix) and the generic decodeSCTPChunk/decodeSCTPDataChunk bounds; the four sub-decoder paths above are unchanged by it. The v1.7.1 regression-test commit 2220269 covers the advisory's other decoders, not SCTP chunk sub-decoders. Affected range is therefore every gopacket version: <= 1.7.1, plus current master 18c5805. (Maintainer update: fixed in v1.7.2, commit 5080443; see Patches.)

Relationship to the published advisory (dedup context). CVE-2026-65819 / GHSA-8mcr-459q-5mx2 (published 2026-07-27) declares vulnerable_version_range: <= 1.7.0 / patched_versions: 1.7.1. Its description explicitly names "SCTP chunk/parameter decoders (sctp.go)" among decoder families "(on the recovering path only) … being confirmed; they should be remediated together with the above". Because the advisory metadata tells consumers that upgrading to 1.7.1 remediates the advisory, and this SCTP component was not actually fixed, the practical status of this finding is incomplete remediation of a published advisory: consumers of the 1.7.1 "patched" version remain exposed to the SCTP panic path. The finding is reported here so the maintainers can either complete the SCTP fix under the existing advisory or track it separately.

Impact

An unauthenticated remote attacker can crash a gopacket-based decoder with a single crafted SCTP segment (IP protocol 132). A 54-byte Ethernet frame (Ethernet -> IPv4 -> SCTP with an 8-byte INIT chunk) is enough (see Reproduction Steps). Panic escapes for consumers that disable gopacket's built-in recovery:

  • gopacket.NewPacket(data, layerType, DecodeOptions{SkipDecodeRecovery: true}) - panic propagates to the caller, terminating the process if uncaught.
  • Direct use of the registered SCTP decode chain without recovery.

Two gopacket entry points install recovery by default and are therefore NOT crash surfaces for this bug: gopacket.NewPacket with default options (deferred recover wraps the whole decode chain, converting the panic into a per-packet error, demonstrated in case B below), and DecodingLayerParser.DecodeLayers when IgnorePanic is false (parser.go:306 - if !l.IgnorePanic { defer panicToError(&err) }); in both cases the packet simply fails to decode. Panic escape - and process crash - requires the non-recovering settings above. This mirrors exactly the reachability of the sibling advisories in the same family (GHSA-6h9g MPTCP, GHSA-8mcr): CVE-2026-65819 itself is scored with the same AV:N/AC:L/PR:N/UI:N vector while noting (per the GHSA advisory text) ~5.3 (Medium) for recovering-path-only consumers; the reporter scored the non-recovering consumer set at 7.5, which is the configuration used by high-throughput packet-analysis engines that set SkipDecodeRecovery to detect decode failures themselves.

The default API (gopacket.NewPacket(..., gopacket.Default)) installs defer recover and converts the panic into a per-packet decode error, so crash exposure is limited to non-recovering consumers (packet-analysis engines, IDS/NDR pipelines, capture-processing tools that set SkipDecodeRecovery or call decoders directly - the same consumer set described in the CVE-2026-65819 and CVE-2026-54332/54345 advisories).

Impact is availability only: no memory disclosure, no code execution, no allocation amplification. Severity: CVSS 3.1 5.9 Medium - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H (reporter proposed 7.5 with AC:L; maintainers set AC:H because a crash needs the consumer to disable panic recovery), primary CWE-125 (out-of-bounds read), secondaries CWE-129 (improper validation of array index for the inverted slices).

Per-metric justification (primary vector):

  • Attack Vector (AV): Network - a single crafted SCTP segment (IP protocol 132) delivered over the wire triggers the panic path; no local access needed.
  • Attack Complexity (AC): High - the input is trivial (a 54-byte frame with an 8-byte INIT chunk), but a crash depends on a condition outside the attacker's control: the target must have disabled gopacket's panic recovery. Default configurations only produce a decode error.
  • Privileges Required (PR): None - the decoding library processes untrusted captured/live packets with no authentication.
  • User Interaction (UI): None - no victim action; the crash occurs during automated decode.
  • Scope (S): Unchanged - the impacted security authority is the decoder's own process (the caller's application).
  • Confidentiality (C): None - no data disclosure.
  • Integrity (I): None - no data modification.
  • Availability (A): High - non-recovering consumer process terminates (panic); the ~5.3 recovering-path-only alternative is disclosed above.

Reproduction Steps

Environment: Go 1.25 toolchain (module requires go 1.25.0), github.com/gopacket/gopacket at HEAD 18c5805028329a6a8944f054d8d3f64ebc0f1373 (v1.7.1 + 1 commit). The same output is produced at tag v1.7.1.

// repro_sctp.go - single static input, one deterministic observable (decode panic)
package main

import (
	"fmt"

	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
)

func main() {
	// One 54-byte frame: Ethernet -> IPv4(proto 132) -> SCTP -> INIT chunk (declared length 8)
	frame := []byte{
		// Ethernet
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00,
		// IPv4, protocol 132 (SCTP), total length 40
		0x45, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x84,
		0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02,
		// SCTP common header (12 bytes)
		0x11, 0x11, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// INIT chunk: type=1, flags=0, length=8 (declared), body 4 bytes
		0x01, 0x00, 0x00, 0x08, 0xaa, 0xbb, 0xcc, 0xdd,
	}
	// same bytes as hex:
	// ffffffffffff00112233445508004500002800000000008400840a0000010a000002
	// 1111222200000000000000000100000008aabbccdd

	// A) Non-recovering decode (SkipDecodeRecovery: true) -> panic escapes
	func() {
		defer func() {
			if r := recover(); r != nil {
				fmt.Printf("A) SkipDecodeRecovery=true  -> PANIC: %v\n", r)
			}
		}()
		p := gopacket.NewPacket(frame, layers.LayerTypeEthernet,
			gopacket.DecodeOptions{Lazy: false, SkipDecodeRecovery: true})
		_ = p.Dump()
		fmt.Println("A) no panic (unexpected)")
	}()

	// B) Default (recovering) API -> per-packet error, process survives
	p := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
	fmt.Printf("B) Default (recovering)    -> err=%v  layers=%d\n", p.ErrorLayer(), len(p.Layers()))
}

Build and run (module file go.mod):

module repro

go 1.25.0

require github.com/gopacket/gopacket v1.7.1

replace github.com/gopacket/gopacket => /path/to/clone/of/gopacket/gopacket  # at 18c5805 (or tag v1.7.1)
$ go build -o repro_sctp repro_sctp.go
$ ./repro_sctp
A) SkipDecodeRecovery=true  -> PANIC: runtime error: slice bounds out of range [:12] with capacity 8
B) Default (recovering)    -> err=Packet decoding error: runtime error: slice bounds out of range [:12] with capacity 8  layers=4

Observed stack (case A): layers.decodeSCTPInit at layers/sctp.go:431 <- SCTPChunkType.Decode <- decodeWithSCTPChunkTypePrefix (sctp.go:57) <- decodeSCTP (sctp.go:38) <- NewPacket.

Additional one-input repros for the other three defect classes (same harness shape, decode root LayerTypeSCTP with SkipDecodeRecovery: true):

Input Panic (observed) Sink
INIT-ACK chunk type 2, declared length 8 slice bounds out of range [:12] with capacity 8 sctp.go:431
INIT chunk declared length 15, 12-byte body slice bounds out of range [:20] with capacity 16 sctp.go:434
SACK chunk type 3, length 16, NumGapACKs=1, NumDuplicateTSNs=1, no trailing bytes slice bounds out of range [:2] with capacity 0 sctp.go:514
SHUTDOWN chunk type 7, declared length 4 slice bounds out of range [:8] with capacity 4 sctp.go:677
ERROR chunk type 9, length 8, 4-byte "parameter" with Length field 0 slice bounds out of range [4:0] sctp.go:198

Recommendations

Complete the v1.7.1 remediation by validating each chunk subtype's declared length against the fixed-size fields and loops that consume it, immediately after decodeSCTPChunk succeeds in each sub-decoder. Minimal correct changes in layers/sctp.go:

  1. decodeSCTPInit: require sc.ActualLength >= 20 before reading data[4:20] (return a decode error otherwise), then bound the parameter loop: paramData := data[20:sc.ActualLength] is safe once the chunk length is ≥ 20 and decodeSCTPParameter is hardened (below).
  2. decodeSCTPShutdown: require sc.ActualLength >= 8.
  3. decodeSCTPSack: require sc.ActualLength >= 16, and cap the loops - iterate min(NumGapACKs, remaining/2) and min(NumDuplicateTSNs, remaining/4), returning an error (or silently stopping with SetTruncated) when a count exceeds the bytes available.
  4. decodeSCTPParameter (shared by INIT/HEARTBEAT/ERROR): reject length < 4 and length > len(data) before the data[4:length] slice, mirroring the bound style already used for TCP options (tcp.go default branch) - this closes Defect 3 at its root regardless of which loop calls it.

Optionally add regression tests in the style of 2220269 covering short INIT/SHUTDOWN/SACK/ERROR chunks.

Resources

Database specific
{
    "cwe_ids": [
        "CWE-125",
        "CWE-129"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-11-10T15:56:49Z",
    "nvd_published_at": "2026-10-30T17:17:18Z",
    "severity": "MODERATE"
}
References

Affected packages

Go / github.com/gopacket/gopacket

Package

Name
github.com/gopacket/gopacket
View open source insights on deps.dev
Purl
pkg:golang/github.com/gopacket/gopacket

Affected ranges

Type
SEMVER
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
1.7.2

Database specific

last_known_affected_version_range
"<= 1.7.1"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-358w-w75h-x6rx/GHSA-358w-w75h-x6rx.json"