โฌข DragonFlyBSD Kernel Audit
DF-0266 / inflate_leak.c
โ† back to finding โ†“ download raw
/*
 * DF-0266 PoC: uninitialized inflate window -> kernel heap info leak.
 *
 * sys/net/zlib.c inflate_blocks_new(:3721) allocates the sliding window via
 * ZALLOC(z,1,w) which resolves to z_alloc() in ng_deflate.c:424-428:
 *
 *   static void *z_alloc(void *notused, u_int items, u_int size) {
 *       return (kmalloc(items * size, M_NETGRAPH_DEFLATE, M_WAITOK | M_NULLOK));
 *       //                                                   ^^^ NO M_ZERO
 *   }
 *
 * inflate_blocks_reset(:3706) resets s->read = s->write = s->window but does
 * NOT zero the window.  inflate_codes() COPY case (zlib.c:4822-4841) copies
 * from the window at position (write - distance) with NO validation that
 * distance <= bytes-actually-output.  A crafted DEFLATE stream that issues a
 * (length, distance) back-reference before any literal output reads from
 * uninitialized window positions -> stale kernel heap bytes emitted as
 * decompressed output.
 *
 * Reachability: netgraph7 PPP deflate (ng_deflate).  A malicious PPP peer
 * that has negotiated CCP deflate sends a compressed frame whose DEFLATE
 * payload back-references the start of the (uninitialized) window.
 *
 * This PoC is a CODE-PATH CONFIRMATION harness.  A live trigger requires a
 * netgraph7 PPP link with deflate compression โ€” not available on the default
 * guest without significant ng_setup.
 *
 * Build:  cc -o inflate_leak inflate_leak.c
 * Run:    ./inflate_leak
 */
#include <stdio.h>
#include <string.h>

int
main(void)
{
	printf("DF-0266: uninitialized inflate window -> heap info leak\n");
	printf("=======================================================\n\n");

	printf("Allocation chain:\n");
	printf("  inflate_blocks_new()  sys/net/zlib.c:3721\n");
	printf("    s->window = ZALLOC(z, 1, w)\n");
	printf("  ZALLOC -> z_alloc()   sys/netgraph7/deflate/ng_deflate.c:427\n");
	printf("    kmalloc(items*size, M_NETGRAPH_DEFLATE, M_WAITOK|M_NULLOK)\n");
	printf("    ^^ NO M_ZERO โ€” window is UNINITIALIZED kernel heap\n\n");

	printf("Reset path (does NOT zero window):\n");
	printf("  inflate_blocks_reset()  sys/net/zlib.c:3706\n");
	printf("    s->read = s->write = s->window;  // pointers only\n\n");

	printf("Leak path โ€” inflate_codes COPY case (sys/net/zlib.c:4822-4841):\n");
	printf("  f = (q - s->window) < dist ?\n");
	printf("        s->end - (dist - (q - s->window)) :  // wrap to end\n");
	printf("        q - dist;\n");
	printf("  while (c->len) { OUTBYTE(*f++); ... }  // copies UNINIT window bytes\n");
	printf("  ^^ NO check that dist <= (q - s->window) + window_size\n\n");

	printf("Attack: malicious PPP peer (after CCP deflate negotiation) sends a\n");
	printf("  compressed frame whose DEFLATE payload starts with a (length,distance)\n");
	printf("  pair before any literal.  distance wraps to end of uninitialized window\n");
	printf("  -> stale heap bytes emitted as 'decompressed' output -> info leak.\n\n");

	printf("Confirmed: z_alloc lacks M_ZERO (ng_deflate.c:427).\n");
	printf("           inflate_blocks_reset doesn't zero window (zlib.c:3706).\n");
	printf("           COPY case has no distance validation (zlib.c:4824).\n");
	printf("Fix: add M_ZERO to z_alloc kmalloc (see fix.diff).\n");
	return 0;
}