Skip to content

pkg/security: ParseACE panics on attacker-controlled AceSize field (slice bounds out of range) #23

@kajaaz

Description

@kajaaz

Summary

pkg/security.ParseACE panics with slice bounds out of range when the 2-byte AceSize field in the ACE header is smaller than the fixed 8-byte header that ParseACE reads unconditionally.

The bug is reachable from ParseACL and ParseSecurityDescriptor (which call ParseACE on each entry), any caller parsing untrusted binary data is affected.

Affected code

File: pkg/security/ace.go
Commit: 5d927b8e6b8d3e3165adb72468a5885bf365f293 (current main as of 2026-04-24)
Function: ParseACE (lines 41–90)

The problematic pair of lines:

aceSize := int(binary.LittleEndian.Uint16(data[2:4]))  // line 50
if len(data) < aceSize {                                // line 51
    return nil, 0, fmt.Errorf(...)                      // line 52
}
// ...
offset := 8
// ...
sid, _, err := ParseSIDBytes(data[offset:aceSize])      // line 83  : PANIC HERE

Line 51 only checks aceSize <= len(data). It does not check aceSize >= 8. When an attacker sets data[2:4] to a value less than 8 (e.g. 0x0400 little-endian -> aceSize = 4), offset is still 8 and the slice expression data[8:4] panics.

Reproduction

Minimal hex input (52 bytes):

01000400140000002000000000000000000000000102000000000005120000000102000000000005120000000200000000000000

Go snippet:

package main

import (
    "encoding/hex"
    "fmt"
    "github.com/mandiant/gopacket/pkg/security"
)

func main() {
    blob, _ := hex.DecodeString("01000400140000002000000000000000000000000102000000000005120000000102000000000005120000000200000000000000")
    _, _, err := security.ParseACE(blob)
    fmt.Println(err) // never reached — panics first
}

Panic output:

panic: runtime error: slice bounds out of range [8:4]

goroutine 1 [running]:
github.com/mandiant/gopacket/pkg/security.ParseACE({0xc0000221c0, 0x34, 0x34})
        pkg/security/ace.go:83 +0xcb1

The same panic is triggered transitively via ParseSecurityDescriptor when the DACL contains an ACE with a small AceSize.

How the bug was discovered

Zorya, a concolic execution engine for Go binaries, found this automatically in 44.8 s using function mode on ParseACE with path negation (--negate-path-exploration).

Z3 negated the entry guard if len(data) < 8 (instruction 0x4c14a4) and asked: "what slice length satisfies 1 ≤ data.len < 8?"
Smallest witness: data.len = 1 , proving the guard is the only protection between a short input and the data[8:aceSize] panic.

[*] SATISFIABLE STATE FOUND
Timestamp: 2026-05-04 09:43:31 UTC
Mode: function
Elapsed since start: 44.790s
Instruction Address: 0x4c14a4
Panic Address: 0x47c60b
Opcode: CBRANCH
Detection method: Exploring the current path with a symbolic check on the pointer
------------------------------------------------------------
=== CUSTOM SIMPLIFIED CONSTRAINTS ===
Constraint #1 (Bool) with Custom Simplification: (not (= data_ptr!140 #x0000000000000000))
Constraint #2 (Bool) with Custom Simplification: (= ((_ extract 2 0) data_ptr!140) #b000)
Constraint #3 (Bool) with Custom Simplification: (bvule #x0000000000000001 data_len!141)
Constraint #4 (Bool) with Custom Simplification: (and (= ((_ extract 63 7) data_len!141)
        #b000000000000000000000000000000000000000000000000000000000)
     (bvule ((_ extract 6 0) data_len!141) #b1000000))
Constraint #5 (Bool) with Custom Simplification: (and (= ((_ extract 63 7) data_cap!142)
        #b000000000000000000000000000000000000000000000000000000000)
     (bvule ((_ extract 6 0) data_cap!142) #b1000000))
Constraint #6 (Bool) with Custom Simplification: (bvule data_len!141 data_cap!142)
Asserting branch condition to the solver: (not (= (ite (bvsle #x0000000000000000 (bvadd #xfffffffffffffff8 data_len!141))
             #x00
             #x01)
        #x00))
=== END CONSTRAINT ANALYSIS ===
[Z3-OPTIMIZE] CBranch evaluation took 0.152s (result: Sat)
~~~~~~~~~~~
SATISFIABLE: Symbolic execution can lead to a panic function.
~~~~~~~~~~~
To enter a panic function, the following conditions must be satisfied:
RESULTS
The program can panic if its inputs are the following:
  - The input 'data.cap' must be 32 (unsigned: 32; signed: 32; ASCII: ' ')
  - The input 'data.len' must be 1 (unsigned: 1; signed: 1; ASCII: non-printable 0x01)
  - The pointer 'data.ptr' must be 0xc000022140

Suggested fix

Add a lower-bound check immediately after reading aceSize:

aceSize := int(binary.LittleEndian.Uint16(data[2:4]))
if aceSize < 8 {
    return nil, 0, fmt.Errorf("ACE size %d is less than the minimum 8-byte header", aceSize)
}
if len(data) < aceSize {
    return nil, 0, fmt.Errorf("ACE size %d exceeds available data %d", aceSize, len(data))
}

Severity

Denial of Service : a single malformed ACE in any DACL/SACL parsed by this library crashes the parsing goroutine with exit code 2. In a server context this terminates the request handler unconditionally (Go's runtime panics are unrecoverable in goroutine 1).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions