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") + } +}