From 679b8435b2472a9693b610d913c7d96a6a94498b Mon Sep 17 00:00:00 2001 From: Vladimir Babin Date: Wed, 2 Sep 2026 15:42:01 +0300 Subject: [PATCH] Reject object ACE whose header exceeds its declared size An object ACE advances the parse offset past the ObjectType and InheritedObjectType GUIDs (up to 28 bytes), bounded only against the input length, not the ACE's own declared AceSize. When AceSize is smaller than that header, data[offset:aceSize] has an inverted slice range and panics. Security descriptors reach ParseACE from attacker-controlled LDAP responses (see pkg/relay), so a crafted ACE crashes the process. Reject offset > aceSize before slicing the SID. --- pkg/security/ace.go | 3 +++ pkg/security/ace_overflow_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 pkg/security/ace_overflow_test.go diff --git a/pkg/security/ace.go b/pkg/security/ace.go index 05db14e..6a414b6 100644 --- a/pkg/security/ace.go +++ b/pkg/security/ace.go @@ -83,6 +83,9 @@ func ParseACE(data []byte) (*ACE, int, error) { } // Parse SID from remaining data + if offset > aceSize { + return nil, 0, fmt.Errorf("ACE object header of %d bytes exceeds ACE size %d", offset, aceSize) + } sid, _, err := ParseSIDBytes(data[offset:aceSize]) if err != nil { return nil, 0, fmt.Errorf("failed to parse ACE SID: %v", err) diff --git a/pkg/security/ace_overflow_test.go b/pkg/security/ace_overflow_test.go new file mode 100644 index 0000000..8654297 --- /dev/null +++ b/pkg/security/ace_overflow_test.go @@ -0,0 +1,27 @@ +package security + +import ( + "encoding/binary" + "testing" +) + +// A malformed object ACE whose AceSize is smaller than the object header it +// declares must be rejected, not panic. ParseACE advances `offset` past the +// object-type GUIDs (up to 28 bytes) but then slices data[offset:aceSize]; +// when aceSize < offset the slice bounds are inverted and Go panics. +func TestParseACE_ObjectAceSizeBelowHeaderNoPanic(t *testing.T) { + data := make([]byte, 32) + data[0] = ACCESS_ALLOWED_OBJECT_ACE_TYPE // object ACE + data[1] = 0 // flags + binary.LittleEndian.PutUint16(data[2:4], 12) // AceSize = 12 (>=8, <= len) + binary.LittleEndian.PutUint32(data[4:8], 0) // Mask + // ObjectFlags at [8:12] with OBJECT_TYPE_PRESENT so offset advances to 28. + binary.LittleEndian.PutUint32(data[8:12], ACE_OBJECT_TYPE_PRESENT) + // data[12:28] is the 16-byte ObjectType GUID (zeros), present in the buffer. + + // Must return an error, not panic. + _, _, err := ParseACE(data) + if err == nil { + t.Fatalf("expected error for object ACE with AceSize below object header") + } +}