{"id":"CVE-2026-119299","aliases":["GHSA-358w-w75h-x6rx"],"url":"https://o3.security/vulnerability/CVE-2026-119299","summary":"gopacket: SCTP chunk sub-decoder out-of-bounds panics on crafted segments","details":"## Summary\n\ngopacket 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.\n\nThe 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.\n\n## Patches\n\nFixed in **v1.7.2** by commit [`5080443`](https://github.com/gopacket/gopacket/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`.\n\nUpgrade with `go get github.com/gopacket/gopacket@v1.7.2`.\n\n## Workarounds\n\nBefore 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.\n\n## Vulnerability Details\n\nAll line numbers refer to `layers/sctp.go` at commit `18c5805028329a6a8944f054d8d3f64ebc0f1373` (master, 2026-09-01; `v1.7.1-1-g18c5805`).\n\nThe 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:\n\n```go\n// sctp.go:129\nfunc decodeSCTPChunk(data []byte) (SCTPChunk, error) {\n\tif len(data) < 4 { ... }\n\tlength := binary.BigEndian.Uint16(data[2:4])\n\tif length < 4 { ... }\n\tactual := roundUpToNearest4(int(length))\n\tct := SCTPChunkType(data[0])\n\tif ct == SCTPChunkTypeData && length < 16 { ... }   // only DATA gets a 16-byte minimum\n\tif len(data) < actual { ... }\n\t...\n```\n\nThe 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`):\n\n**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).\n- Declared length 8–11, actual 8–12 -> `data[8:12]` reads past the chunk:\n  `panic: runtime error: slice bounds out of range [:12] with capacity 8`\n- Declared length 15, actual 16 -> `data[16:20]` past the chunk:\n  `panic: runtime error: slice bounds out of range [:20] with capacity 16`\n- 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).\n\n**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:\n`panic: runtime error: slice bounds out of range [:8] with capacity 4` (4-byte chunk).\n\n**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:\n\n```go\n// sctp.go:193\nfunc decodeSCTPParameter(data []byte) SCTPParameter {\n\tlength := binary.BigEndian.Uint16(data[2:4])\n\treturn SCTPParameter{\n\t\t...\n\t\tValue:        data[4:length],   // line 198: no check length >= 4 or length <= len(data)\n\t\tActualLength: roundUpToNearest4(int(length)),\n\t}\n}\n```\n\n- `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).\n- `length > len(data)` -> slice-bounds-check panic (Go's slice bounds check panics; the read never silently over-reads).\n\n**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:\n\n```go\n// sctp.go:512\nbytesRemaining := data[16:]\nfor i := 0; i < int(sc.NumGapACKs); i++ {                       // line 513\n\tsc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2]))  // line 514\n\tbytesRemaining = bytesRemaining[2:]\n}\nfor i := 0; i < int(sc.NumDuplicateTSNs); i++ {                 // line 517\n\tsc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4]))  // line 518\n\tbytesRemaining = bytesRemaining[4:]\n}\n```\n\nA 16-byte SACK chunk declaring `NumGapACKs = 1` with no following bytes:\n`panic: runtime error: slice bounds out of range [:2] with capacity 0`\n\nNote 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.\n\n**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.)*\n\n**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.\n\n## Impact\n\nAn 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:\n\n- `gopacket.NewPacket(data, layerType, DecodeOptions{SkipDecodeRecovery: true})` - panic propagates to the caller, terminating the process if uncaught.\n- Direct use of the registered SCTP decode chain without recovery.\n\nTwo 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.\n\nThe 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).\n\nImpact 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).\n\nPer-metric justification (primary vector):\n- **Attack Vector (AV): Network** - a single crafted SCTP segment (IP protocol 132) delivered over the wire triggers the panic path; no local access needed.\n- **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.\n- **Privileges Required (PR): None** - the decoding library processes untrusted captured/live packets with no authentication.\n- **User Interaction (UI): None** - no victim action; the crash occurs during automated decode.\n- **Scope (S): Unchanged** - the impacted security authority is the decoder's own process (the caller's application).\n- **Confidentiality (C): None** - no data disclosure.\n- **Integrity (I): None** - no data modification.\n- **Availability (A): High** - non-recovering consumer process terminates (panic); the ~5.3 recovering-path-only alternative is disclosed above.\n\n## Reproduction Steps\n\nEnvironment: 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`.\n\n```go\n// repro_sctp.go - single static input, one deterministic observable (decode panic)\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gopacket/gopacket\"\n\t\"github.com/gopacket/gopacket/layers\"\n)\n\nfunc main() {\n\t// One 54-byte frame: Ethernet -> IPv4(proto 132) -> SCTP -> INIT chunk (declared length 8)\n\tframe := []byte{\n\t\t// Ethernet\n\t\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00,\n\t\t// IPv4, protocol 132 (SCTP), total length 40\n\t\t0x45, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x84,\n\t\t0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02,\n\t\t// SCTP common header (12 bytes)\n\t\t0x11, 0x11, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t// INIT chunk: type=1, flags=0, length=8 (declared), body 4 bytes\n\t\t0x01, 0x00, 0x00, 0x08, 0xaa, 0xbb, 0xcc, 0xdd,\n\t}\n\t// same bytes as hex:\n\t// ffffffffffff00112233445508004500002800000000008400840a0000010a000002\n\t// 1111222200000000000000000100000008aabbccdd\n\n\t// A) Non-recovering decode (SkipDecodeRecovery: true) -> panic escapes\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tfmt.Printf(\"A) SkipDecodeRecovery=true  -> PANIC: %v\\n\", r)\n\t\t\t}\n\t\t}()\n\t\tp := gopacket.NewPacket(frame, layers.LayerTypeEthernet,\n\t\t\tgopacket.DecodeOptions{Lazy: false, SkipDecodeRecovery: true})\n\t\t_ = p.Dump()\n\t\tfmt.Println(\"A) no panic (unexpected)\")\n\t}()\n\n\t// B) Default (recovering) API -> per-packet error, process survives\n\tp := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)\n\tfmt.Printf(\"B) Default (recovering)    -> err=%v  layers=%d\\n\", p.ErrorLayer(), len(p.Layers()))\n}\n```\n\nBuild and run (module file `go.mod`):\n\n```\nmodule repro\n\ngo 1.25.0\n\nrequire github.com/gopacket/gopacket v1.7.1\n\nreplace github.com/gopacket/gopacket => /path/to/clone/of/gopacket/gopacket  # at 18c5805 (or tag v1.7.1)\n```\n\n```\n$ go build -o repro_sctp repro_sctp.go\n$ ./repro_sctp\nA) SkipDecodeRecovery=true  -> PANIC: runtime error: slice bounds out of range [:12] with capacity 8\nB) Default (recovering)    -> err=Packet decoding error: runtime error: slice bounds out of range [:12] with capacity 8  layers=4\n```\n\nObserved stack (case A): `layers.decodeSCTPInit` at `layers/sctp.go:431` <- `SCTPChunkType.Decode` <- `decodeWithSCTPChunkTypePrefix` (`sctp.go:57`) <- `decodeSCTP` (`sctp.go:38`) <- `NewPacket`.\n\nAdditional one-input repros for the other three defect classes (same harness shape, decode root `LayerTypeSCTP` with `SkipDecodeRecovery: true`):\n\n| Input | Panic (observed) | Sink |\n|---|---|---|\n| INIT-ACK chunk type 2, declared length 8 | `slice bounds out of range [:12] with capacity 8` | sctp.go:431 |\n| INIT chunk declared length 15, 12-byte body | `slice bounds out of range [:20] with capacity 16` | sctp.go:434 |\n| 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 |\n| SHUTDOWN chunk type 7, declared length 4 | `slice bounds out of range [:8] with capacity 4` | sctp.go:677 |\n| ERROR chunk type 9, length 8, 4-byte \"parameter\" with Length field 0 | `slice bounds out of range [4:0]` | sctp.go:198 |\n\n## Recommendations\n\nComplete 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`:\n\n1. **`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).\n2. **`decodeSCTPShutdown`**: require `sc.ActualLength >= 8`.\n3. **`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.\n4. **`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.\n\nOptionally add regression tests in the style of `2220269` covering short INIT/SHUTDOWN/SACK/ERROR chunks.\n\n## Resources\n\n- Source at pin: `layers/sctp.go` @ https://github.com/gopacket/gopacket/blob/18c5805028329a6a8944f054d8d3f64ebc0f1373/layers/sctp.go (lines 129–136 dispatcher/chunk guard, 193–202 `decodeSCTPParameter`, 423–442 `decodeSCTPInit`, 484–521 `decodeSCTPSack`, 670–678 `decodeSCTPShutdown`, 623–636 `decodeSCTPError`)\n- Fix commit that missed the sub-decoders: https://github.com/gopacket/gopacket/commit/b772f566a3ecc53c099bdd44eab4718088c4e866 (PR #163, \"layers:various: add protocol decoder length checks\")\n- Regression-test commit: https://github.com/gopacket/gopacket/commit/2220269 (v1.7.1, \"test(layers): add regression tests for the decoder panic advisories\")\n- Advisory that names the unfixed SCTP component: https://github.com/gopacket/gopacket/security/advisories/GHSA-8mcr-459q-5mx2 (CVE-2026-65819, \"Multiple layer decoders panic on crafted packets …, including (on the recovering path only) … SCTP chunk/parameter decoders (sctp.go)\"); NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-65819\n- Sibling advisory with identical reachability framing (High 7.5): https://github.com/gopacket/gopacket/security/advisories/GHSA-6h9g-cjv3-pg2c (TCP MPTCP option decoder)\n- Introduced in: https://github.com/gopacket/gopacket/commit/2fca351 (original SCTP implementation)","published":"2026-10-30T16:23:51.655Z","modified":"2026-11-01T02:47:27.809396932Z","cvss":{"score":5.9,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"Go","name":"github.com/gopacket/gopacket","fixedVersion":"1.7.2"}],"fix":{"url":"https://github.com/gopacket/gopacket/commit/508044327cc2c065b3db753c010a027e7d1ca621","label":"gopacket/gopacket@5080443"},"references":[{"type":"WEB","url":"https://github.com/gopacket/gopacket/releases/tag/v1.7.2"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/119xxx/CVE-2026-119299.json"},{"type":"ADVISORY","url":"https://github.com/gopacket/gopacket/security/advisories/GHSA-358w-w75h-x6rx"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-119299"},{"type":"FIX","url":"https://github.com/gopacket/gopacket/commit/508044327cc2c065b3db753c010a027e7d1ca621"},{"type":"PACKAGE","url":"https://github.com/gopacket/gopacket"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-11-01T02:47:27.809396932Z"}}