Package: github.com/mandiant/gopacket/pkg/registry
Affected file: pkg/registry/hive.go
Commit tested: 5d927b8
Summary
(*Hive).GetValueData can panic with a slice-bounds-out-of-range error when parsing a registry hive whose VKRecord.DataLen encodes a resident value with a length greater than 4. Because DataLen is read verbatim from hive bytes with no prior range check, any caller that parses an attacker-supplied hive file is exposed to a denial-of-service crash.
Root cause
The Windows Registry format uses bit 31 of VKRecord.DataLen as a "resident" flag: when set, the actual data (up to 4 bytes) is stored inline in DataOffset rather than in a separate cell. The relevant code in GetValueData is:
// Data is stored in the DataOffset field itself (up to 4 bytes)
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, vk.DataOffset)
return data[:dataLen], nil
The comment documents the invariant ("up to 4 bytes"), but no guard enforces it. dataLen is derived as vk.DataLen & 0x7FFFFFFF, and vk.DataLen is populated by parseVK directly from raw hive bytes:
binary.Read(r, binary.LittleEndian, &vk.DataLen) // no range check
If the hive contains DataLen = 0x80000005 (resident flag set, lower 31 bits = 5), then dataLen becomes 5 and data[:5] panics on the 4-byte slice.
Steps to reproduce
package main
import (
"fmt"
"encoding/binary"
"github.com/mandiant/gopacket/pkg/registry"
)
func main() {
// Minimal valid hive: regf magic + rootOffset at offset 36
hive := make([]byte, 4096+64)
copy(hive, []byte{0x72, 0x65, 0x67, 0x66}) // "regf"
h, err := registry.Open(hive)
if err != nil {
panic(err)
}
vk := ®istry.VKRecord{
Signature: 0x6B76,
DataLen: 0x80000005, // isResident=true, dataLen=5 --> data[:5] on a 4-byte slice
DataOffset: 0x01020304,
DataType: 1,
}
data, err := h.GetValueData(vk) // panic: runtime error: slice bounds out of range [:5] with capacity 4
fmt.Println(data, err)
}
Panic output:
panic: runtime error: slice bounds out of range [:5] with capacity 4
goroutine 1 [running]:
github.com/mandiant/gopacket/pkg/registry.(*Hive).GetValueData(0x37f9430a0000?, 0x37f943092f10)
/tmp/gopath1113421067/pkg/mod/github.com/mandiant/gopacket@v0.0.0-20260424163850-5d927b8e6b8d/pkg/registry/hive.go:258 +0xb0
main.main()
/tmp/sandbox3190411761/prog.go:26 +0x9f
How the bug was found
This bug was discovered using Zorya, a concolic execution engine for ELF binaries. Zorya combines a GDB-based concrete execution snapshot with P-code symbolic execution and the Z3 SMT solver to find inputs that drive a program to a crash site.
A test harness was written that called the resident-data path of GetValueData directly, passing dataLen and dataOffset as arguments:
//go:noinline
func exerciseResident(dataLen uint32, dataOffset uint32) {
data := make([]byte, 4)
data[0] = byte(dataOffset)
// ...
result := data[:dataLen] // target: slice bounds panic
fmt.Printf("resident ok len=%d\n", len(result))
}
Zorya was pointed at this function in --mode function, which snapshotted the process at the function entry, promoted dataLen and dataOffset to symbolic Z3 variables, and ran concolic analysis:
zorya path/to/bin \
--mode function 0x4a9560 \
--lang go --compiler gc \
--thread-scheduling main-only \
--arg "72656766000000000000000000000000000000000000000000000000000000000000000000000000" \
--negate-path-exploration
(0x4a9560 is the address of main.exerciseResident in the compiled harness binary; --arg is a minimal valid regf header that lets the harness reach the function without error.)
Within 44 seconds it produced the following witness:
[*] SATISFIABLE STATE FOUND
Timestamp: 2026-05-11 08:02:00 UTC
Elapsed since start: 44.661s
Instruction Address: 0x4a9574
Panic Address: 0x4a95cb
Opcode: CBRANCH
Detection method: Exploring the not taken path with Overlay Execution
The program can panic if its inputs are the following:
- The input 'dataLen' must be 5 (unsigned: 5; signed: 5)
The CBRANCH oracle detected that the slice bounds check at 0x4a9574 branches to a runtime.panicSliceB call site (0x4a95cb) when dataLen > 4, and Z3 solved the single constraint dataLen ≥ 5 to produce the minimal witness dataLen = 5.
Impact
Any application that uses this package to parse registry hives from untrusted sources (forensics pipelines, EDR agents, malware analysis tooling) can be crashed by providing a single malformed VK record with DataLen = 0x80000005. The crash is deterministic and requires no special privileges.
Suggested fix
Add a bounds check before the slice expression:
if isResident {
if dataLen > 4 {
return nil, fmt.Errorf("resident data length %d exceeds maximum of 4 bytes", dataLen)
}
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, vk.DataOffset)
return data[:dataLen], nil
}
This is consistent with the existing comment ("up to 4 bytes") and matches the Windows Registry format specification, which states that inline resident values are limited to 4 bytes.
Package:
github.com/mandiant/gopacket/pkg/registryAffected file:
pkg/registry/hive.goCommit tested:
5d927b8Summary
(*Hive).GetValueDatacan panic with a slice-bounds-out-of-range error when parsing a registry hive whoseVKRecord.DataLenencodes a resident value with a length greater than 4. BecauseDataLenis read verbatim from hive bytes with no prior range check, any caller that parses an attacker-supplied hive file is exposed to a denial-of-service crash.Root cause
The Windows Registry format uses bit 31 of
VKRecord.DataLenas a "resident" flag: when set, the actual data (up to 4 bytes) is stored inline inDataOffsetrather than in a separate cell. The relevant code inGetValueDatais:The comment documents the invariant ("up to 4 bytes"), but no guard enforces it.
dataLenis derived asvk.DataLen & 0x7FFFFFFF, andvk.DataLenis populated byparseVKdirectly from raw hive bytes:If the hive contains
DataLen = 0x80000005(resident flag set, lower 31 bits = 5), thendataLenbecomes 5 anddata[:5]panics on the 4-byte slice.Steps to reproduce
Panic output:
How the bug was found
This bug was discovered using Zorya, a concolic execution engine for ELF binaries. Zorya combines a GDB-based concrete execution snapshot with P-code symbolic execution and the Z3 SMT solver to find inputs that drive a program to a crash site.
A test harness was written that called the resident-data path of
GetValueDatadirectly, passingdataLenanddataOffsetas arguments:Zorya was pointed at this function in
--mode function, which snapshotted the process at the function entry, promoteddataLenanddataOffsetto symbolic Z3 variables, and ran concolic analysis:(
0x4a9560is the address ofmain.exerciseResidentin the compiled harness binary;--argis a minimal valid regf header that lets the harness reach the function without error.)Within 44 seconds it produced the following witness:
The CBRANCH oracle detected that the slice bounds check at
0x4a9574branches to aruntime.panicSliceBcall site (0x4a95cb) whendataLen > 4, and Z3 solved the single constraintdataLen ≥ 5to produce the minimal witnessdataLen = 5.Impact
Any application that uses this package to parse registry hives from untrusted sources (forensics pipelines, EDR agents, malware analysis tooling) can be crashed by providing a single malformed VK record with
DataLen = 0x80000005. The crash is deterministic and requires no special privileges.Suggested fix
Add a bounds check before the slice expression:
This is consistent with the existing comment ("up to 4 bytes") and matches the Windows Registry format specification, which states that inline resident values are limited to 4 bytes.