DF-0269 / sppp_vla.c
/* * DF-0269 PoC: sppp_print_bytes VLA stack buffer overflow. * * sys/net/sppp/if_spppsubr.c:5287-5293: * * static void sppp_print_bytes(const u_char *p, u_short len) { * char hexstr[len]; // <-- VLA: len bytes * if (len) * log(-1, " %s", hexncpy(p, len, hexstr, HEX_NCPYLEN(len), "-")); * } * * HEX_NCPYLEN(s) = (s * 3) [sys/sys/libkern.h:67] * hexncpy() (sys/libkern/hexncpy.c:56-61) writes 3 bytes per input byte: * for (; inlen > 0 && outlen >= 3; --inlen, outlen -= 3) { * *outb++ = hexdigit[*inb >> 4]; * *outb++ = hexdigit[*inb++ & 0xf]; * *outb++ = *sep; // separator * } * * So hexncpy writes 3*len bytes into hexstr[len] -> 2*len bytes stack overflow. * * Reachability (pre-auth): sppp_cp_input() (if_spppsubr.c:1399-1410): * if (debug) { // debug = ifp->if_flags & IFF_DEBUG * printlen = ntohs(h->len); * ... * if (printlen > 4) * sppp_print_bytes((u_char*)(h+1), printlen - 4); * } * This runs in PHASE_ESTABLISH (LCP negotiation, BEFORE PHASE_AUTHENTICATE). * A peer sending an LCP frame to a sppp interface with IFF_DEBUG set triggers * the overflow. For a ~1500-byte frame, printlen ~= 1496 -> ~2992 bytes overflow. * * This PoC is a CODE-PATH CONFIRMATION harness. A live trigger requires a * sppp-based interface (PPPoE/sync-serial) with IFF_DEBUG — the sppp module * is a framework, not directly cloneable (ifconfig sppp0 create fails). * * Build: cc -o sppp_vla sppp_vla.c * Run: ./sppp_vla */ #include <stdio.h> #define HEX_NCPYLEN(s) (s * 3) int main(void) { /* Simulate the VLA overflow to demonstrate the size mismatch */ unsigned short len = 100; /* e.g., a 100-byte LCP options payload */ char hexstr_len[len]; /* what the kernel allocates: 100 bytes */ int needed = HEX_NCPYLEN(len); /* what hexncpy writes: 300 bytes */ printf("DF-0269: sppp_print_bytes VLA stack overflow\n"); printf("============================================\n\n"); printf("sppp_print_bytes (if_spppsubr.c:5290):\n"); printf(" char hexstr[len]; // allocates %u bytes\n", len); printf(" hexncpy(p, len, hexstr, HEX_NCPYLEN(len), \"-\");\n"); printf(" HEX_NCPYLEN(%u) = %u // hexncpy writes %u bytes\n", len, needed, needed); printf(" OVERFLOW = %u - %u = %u bytes past the VLA buffer\n\n", needed, len, needed - len); printf("For a 1500-byte PPP frame (printlen-4 ~= 1496):\n"); printf(" VLA size: 1496 bytes\n"); printf(" hexncpy writes: 4488 bytes\n"); printf(" OVERFLOW: 2992 bytes of kernel stack smashed\n\n"); printf("Pre-auth reachability:\n"); printf(" sppp_cp_input (if_spppsubr.c:1399-1410) runs in PHASE_ESTABLISH\n"); printf(" (LCP negotiation) BEFORE PHASE_AUTHENTICATE.\n"); printf(" Triggered by: IFF_DEBUG set + peer sends LCP frame with len>4.\n\n"); printf("sppp is not directly cloneable (ifconfig sppp0 create -> EINVAL).\n"); printf("Requires PPPoE/sync-serial interface using the sppp framework.\n"); printf("Fix: char hexstr[HEX_NCPYLEN(len)] (see fix.diff).\n"); return 0; } |