DF-0672 / df0672_uninit_sin.c
/* * DF-0672 PoC — userspace structural demonstration of uninitialized * struct sockaddr_in in nbssn_rq_request retarget branch. * * The vulnerable code lives in sys/netproto/smb/smb_trantcp.c:206-263 * and is built into the smbfs.ko module (loaded via `kldload smbfs`). * Triggering the bug LIVE requires a malicious NBSSN server that * responds with NB_SSN_RTGRESP and a victim user running mount_smbfs * (or otherwise opening a kernel SMB session); see VERDICT.md for why * that's beyond a minimal PoC. * * This harness reproduces the STRUCTURAL bug at the C level: it * declares `struct sockaddr_in sin;` on the stack with NO init (as * the kernel does), fills only sin.sin_addr (4B) and sin.sin_port * (2B) as the retarget branch does, then prints the full struct. The * sin_len, sin_family, and sin_zero[8] fields contain stack garbage * in every run — exactly the kernel-side defect. * * Build: cc -O -pipe -o df0672_uninit_sin df0672_uninit_sin.c * Run: ./df0672_uninit_sin * * Expected: sin.sin_len and sin.sin_family are non-deterministic * (stack garbage), and sin_zero[8] contains residue. Each run varies. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> int main(void) { int i; /* Mirror sys/netproto/smb/smb_trantcp.c:206: * struct sockaddr_in sin; <-- NO initialization * The kernel stack contains residue from prior call frames. */ struct sockaddr_in sin; /* uninitialized, like the kernel */ /* Prime the stack with garbage so the demo is reproducible. */ for (i = 0; i < 16; i++) { volatile unsigned char garbage[64]; memset((void *)garbage, 0xa0 + i, sizeof garbage); } /* Re-declare sin AFTER priming to land it on top of the residue. */ { struct sockaddr_in sin2; sin = sin2; /* copy residue-filled stack */ } /* Retarget branch (smb_trantcp.c:258-260) fills only these fields: */ { unsigned char addr[4] = { 192, 168, 1, 100 }; unsigned short port_net = htons(139); memcpy(&sin.sin_addr, addr, 4); /* md_get_mem 4B */ memcpy(&sin.sin_port, &port_net, 2); /* md_get_uint16 2B */ } /* in_pcbladdr_find (netinet/in_pcb.c:954-957) then validates: * if (nam->sa_len != sizeof *sin) return EINVAL; * if (sin->sin_family != AF_INET) return EAFNOSUPPORT; * With uninit sin_len and sin_family, behavior is UB: usually the * connect returns EINVAL/EAFNOSUPPORT silently, occasionally * (stack residue matches by chance) the connect proceeds with * attacker-chosen addr/port via a corrupted sockaddr. */ printf("Run result (sin on stack, only sin_addr/sin_port set):\n"); printf(" sin.sin_len = 0x%02x (expect 0x10=16; needs AF_INET path)\n", (unsigned)sin.sin_len); printf(" sin.sin_family = 0x%04x (expect 0x0002=AF_INET)\n", (unsigned)sin.sin_family); printf(" sin.sin_port = 0x%04x (set by retarget branch)\n", (unsigned)sin.sin_port); printf(" sin.sin_addr = 0x%08x (set by retarget branch)\n", (unsigned)sin.sin_addr.s_addr); printf(" sin.sin_zero = "); for (i = 0; i < 8; i++) printf("%02x", (unsigned)sin.sin_zero[i]); printf(" (UNINIT residue)\n"); if (sin.sin_len == 16 && sin.sin_family == AF_INET) { printf(" -> By chance sin_len=16 and family=AF_INET this run:\n" " the connect would PROCEED with attacker-chosen target!\n"); } else { printf(" -> sin_len or sin_family mismatched this run:\n" " in_pcbladdr_find would return EINVAL/EAFNOSUPPORT.\n"); } return 0; } |