Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions attest/internal/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,26 @@ func parseEfiSignatureList(b []byte) ([]x509.Certificate, [][]byte, error) {
if signatures.Header.SignatureSize > remainingListSize {
return nil, nil, fmt.Errorf("SignatureSize %d exceeds remaining signature list space %d", signatures.Header.SignatureSize, remainingListSize)
}
// Guard against hash injection via oversized SignatureHeaderSize.
// Per UEFI spec section 31.4.1, SignatureHeaderSize bytes of vendor data
// appear between the fixed header and the actual signature entries.
// SignatureHeaderSize must not consume the entire remaining list space.
if signatures.Header.SignatureHeaderSize >= remainingListSize {
return nil, nil, fmt.Errorf("SignatureHeaderSize %d exceeds remaining signature list space %d", signatures.Header.SignatureHeaderSize, remainingListSize)
}
// Skip the vendor-specific SignatureHeader bytes per UEFI spec section 31.4.1.
// Without this, vendor bytes are misread as signature entries, allowing a
// crafted event log to inject arbitrary hashes into the trusted hash list.
if signatures.Header.SignatureHeaderSize > 0 {
if _, err := buf.Seek(int64(signatures.Header.SignatureHeaderSize), io.SeekCurrent); err != nil {
return nil, nil, fmt.Errorf("seeking past signature vendor header: %w", err)
}
}

signatureType := signatures.Header.SignatureType
switch signatureType {
case certX509SigGUID: // X509 certificate
for sigOffset := 0; uint32(sigOffset) < signatures.Header.SignatureListSize-efiSignatureListHeaderSize; {
for sigOffset := int(signatures.Header.SignatureHeaderSize); uint32(sigOffset) < signatures.Header.SignatureListSize-efiSignatureListHeaderSize; {
signature := efiSignatureData{}
signature.SignatureData = make([]byte, signatures.Header.SignatureSize-efiGUIDSize)
err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner)
Expand All @@ -404,7 +419,7 @@ func parseEfiSignatureList(b []byte) ([]x509.Certificate, [][]byte, error) {
certificates = append(certificates, *cert)
}
case hashSHA256SigGUID: // SHA256
for sigOffset := 0; uint32(sigOffset) < signatures.Header.SignatureListSize-efiSignatureListHeaderSize; {
for sigOffset := int(signatures.Header.SignatureHeaderSize); uint32(sigOffset) < signatures.Header.SignatureListSize-efiSignatureListHeaderSize; {
signature := efiSignatureData{}
signature.SignatureData = make([]byte, signatures.Header.SignatureSize-efiGUIDSize)
err := binary.Read(buf, binary.LittleEndian, &signature.SignatureOwner)
Expand Down
52 changes: 52 additions & 0 deletions attest/internal/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,55 @@ func TestParseUEFIVariableData(t *testing.T) {
t.Errorf("ParseUEFIVariableData() mismatch (-want +got):\n%s", diff)
}
}

func TestParseEfiSignatureListOversizedSignatureHeaderSize(t *testing.T) {
sigType := [16]byte{
0x26, 0x16, 0xc4, 0xc1, 0x4c, 0x50, 0x92, 0x40,
0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28,
}
const (
sha256HashSize = 32
sigSize = efiGUIDSize + sha256HashSize
)
// sigHeaderSize == remainingListSize: consumes all remaining space.
// The bound check must reject this.
sigHeaderSize := sigSize
sigListSize := uint32(efiSignatureListHeaderSize + sigHeaderSize)
data := buildEFISignatureListData(sigType, sigListSize, uint32(sigHeaderSize), sigSize, 0)
_, _, err := parseEfiSignatureList(data)
if err == nil {
t.Error("parseEfiSignatureList() accepted oversized SignatureHeaderSize, want error")
}
}

func TestParseEfiSignatureListVendorHeaderNotTrusted(t *testing.T) {
sigType := [16]byte{
0x26, 0x16, 0xc4, 0xc1, 0x4c, 0x50, 0x92, 0x40,
0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28,
}
const (
sha256HashSize = 32
sigSize = efiGUIDSize + sha256HashSize
vendorHeaderSize = sha256HashSize // smaller than sigSize so bound check passes
)
// Attacker-controlled vendor header bytes.
attackerBytes := bytes.Repeat([]byte{0xAA}, vendorHeaderSize)
// One legitimate entry.
legitHash := bytes.Repeat([]byte{0xBB}, sha256HashSize)
legitEntry := make([]byte, sigSize)
copy(legitEntry[efiGUIDSize:], legitHash)
sigListSize := uint32(efiSignatureListHeaderSize + vendorHeaderSize + len(legitEntry))
data := buildEFISignatureListData(sigType, sigListSize, vendorHeaderSize, sigSize, 0)
data = append(data, attackerBytes...)
data = append(data, legitEntry...)
_, hashes, err := parseEfiSignatureList(data)
if err != nil {
t.Fatalf("parseEfiSignatureList() returned unexpected error: %v", err)
}
if len(hashes) != 1 {
t.Fatalf("parseEfiSignatureList returned %d hashes, expected 1, hashes: %v", len(hashes), hashes)
}
if !bytes.Equal(hashes[0], legitHash) {
t.Errorf("parseEfiSignatureList returned hash %x, expected %x", hashes[0], legitHash)
}
}
Loading