DF-0266 / inflate_leak.c
/* * 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; } |