DF-0983 / urtwn_efuse_harness.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 | /* * DF-0983 โ userspace harness replicating the kernel's efuse-parsing logic * found in sys/bus/u4b/wlan/if_urtwn.c (urtwn_efuse_read + urtwn_efuse_read_data). * * The kernel reads efuse bytes from a (malicious) RTL8188EU USB dongle and uses * an attacker-controlled `off` byte to index into `sc->rom` (a 512-byte * union urtwn_rom embedded in struct urtwn_softc). There is NO bounds check * tying `off` to the 512-byte rom size, so a crafted extended-header pair * (first_reg=0xef, second_reg=0xf0) yields off=127 and writes land at * rom[127*8 + i*2 + {0,1}] = rom[1016..1023] โ 504..511 bytes past the * 512-byte buffer, corrupting the softc fields that follow (last_rom_addr, * callouts, sc_mtx driver lock). * * This harness reproduces that exact indexing on a faithful buffer model and * shows the out-of-bounds write. Run twice: once as-is (VULNERABLE) and once * compiled with -DFIXED to confirm the proposed bounds check eliminates it. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define URTWN_CHIP_88E 0x10 #define URTWN_EFUSE_SIZE 512 /* union urtwn_rom == 512 bytes */ #define SOFTC_TAIL 512 /* bytes after rom inside softc (model) */ /* Faithful copy of urtwn_efuse_read_data (if_urtwn.c:1755). */ static void efuse_read_data(uint8_t *rom, uint8_t off, uint8_t msk, const uint8_t **p /* efuse stream cursor */) { uint8_t reg; for (int i = 0; i < 4; i++) { if (msk & (1 << i)) continue; reg = *(*p)++; /* urtwn_efuse_read_next */ rom[off * 8 + i * 2 + 0] = reg; reg = *(*p)++; rom[off * 8 + i * 2 + 1] = reg; } } int main(void) { /* Model the softc region: [rom(512)] [tail(512)]. rom == sc->rom. */ uint8_t softc[URTWN_EFUSE_SIZE + SOFTC_TAIL]; uint8_t *rom = softc; memset(softc, 0x00, sizeof(softc)); /* mark the tail as canary so we can see the overflow */ memset(softc + URTWN_EFUSE_SIZE, 0xCC, SOFTC_TAIL); uint32_t chip = URTWN_CHIP_88E; /* * Malicious efuse stream. The kernel reads one byte, loops while != 0xff. * byte 0 = 0xef -> (reg & 0x1f)==0x0f -> extended-header branch * off = reg>>5 = 7 * byte 1 = 0xf0 -> (reg & 0x0f)=0 != 0x0f * off = ((0xf0)>>1)|7 = 0x78|0x07 = 127 * msk = 0xf0 & 0xf = 0 (all 4 words written) * (efuse_read_data consumes 8 more bytes, one per rom slot) * byte 2..9 -> payload written to rom[1016..1023] * byte 10 = 0xff -> loop exits */ uint8_t efuse[] = { 0xef, 0xf0, 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0xff }; const uint8_t *cur = efuse; const uint8_t *stream_end = efuse + sizeof(efuse); uint8_t reg = *cur++; /* first read, before loop */ int overflows = 0; unsigned max_idx = 0; while (reg != 0xff) { uint8_t off, msk; if ((chip & URTWN_CHIP_88E) && (reg & 0x1f) == 0x0f) { off = reg >> 5; reg = *cur++; /* read NEXT reg */ if ((reg & 0x0f) != 0x0f) off = ((reg & 0xf0) >> 1) | off; else { reg = *cur++; continue; } } else { off = reg >> 4; } msk = reg & 0xf; #ifdef FIXED /* Proposed fix: bounds-check off against the rom size. */ if ((unsigned)off * 8 + 7 >= URTWN_EFUSE_SIZE) { printf("FIX: off=%u would overflow rom[%u..%u] (size %d) -> skipped\n", off, off*8, off*8+7, URTWN_EFUSE_SIZE); reg = *cur++; continue; } #endif /* track the indices BEFORE writing (kernel writes unconditionally) */ for (int i = 0; i < 4; i++) { if (msk & (1 << i)) continue; unsigned idx0 = off*8 + i*2 + 0, idx1 = off*8 + i*2 + 1; if (idx0 >= URTWN_EFUSE_SIZE) { overflows++; if (idx0>max_idx) max_idx=idx0; } if (idx1 >= URTWN_EFUSE_SIZE) { overflows++; if (idx1>max_idx) max_idx=idx1; } } efuse_read_data(rom, off, msk, &cur); reg = *cur++; if (cur > stream_end) break; /* safety for harness */ } printf("chip=0x%x rom_size=%d\n", chip, URTWN_EFUSE_SIZE); printf("crafted off=127 -> writes rom[1016..1023] (0x%03X..0x%03X)\n", 1016, 1023); printf("OOB writes past rom end: %d (max index %u)\n", overflows, max_idx); /* inspect the canary tail (bytes 504..511 past the rom are rom[1016..1023]) */ int hit = 0; for (int i = URTWN_EFUSE_SIZE; i < (int)sizeof(softc); i++) { if (rom[i] != 0xCC) { printf(" softc->rom+%d (rom[%d]) OVERWRITTEN = 0x%02x\n", i - URTWN_EFUSE_SIZE, i, rom[i]); hit = 1; } } if (overflows > 0 && hit) printf("RESULT: OVERFLOW CONFIRMED โ 8 bytes written %d past sc->rom " "into softc tail (kernel: last_rom_addr/callouts/sc_mtx)\n", 1016 - URTWN_EFUSE_SIZE); else if (overflows == 0) printf("RESULT: NO OVERFLOW (fix bounds check active)\n"); else printf("RESULT: indexing OOB but canary intact (unexpected)\n"); return (overflows > 0) ? 1 : 0; } |