From 36bea16af717b3ffd8cf7ddc6ec006edfb963474 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 27 Oct 2013 06:33:49 +0000 Subject: [PATCH] IPv6 idle scan patch by Mathias Morbitzer. http://seclists.org/nmap-dev/2013/q3/549 --- NmapOps.cc | 2 +- idle_scan.cc | 512 ++++++++++++++++++++++++++++++++++++++++++--------- nmap.cc | 2 + nmap.h | 1 + osscan2.cc | 99 +++++++--- osscan2.h | 5 +- 6 files changed, 505 insertions(+), 116 deletions(-) diff --git a/NmapOps.cc b/NmapOps.cc index 0b8c8c1cd..9837d8d6c 100644 --- a/NmapOps.cc +++ b/NmapOps.cc @@ -565,7 +565,7 @@ dialog where you can start NPF if you have administrator privileges."; fatal("--min-rate=%g must be less than or equal to --max-rate=%g", min_packet_send_rate, max_packet_send_rate); } - if (af() == AF_INET6 && (generate_random_ips|numdecoys|bouncescan|fragscan|idlescan)) { + if (af() == AF_INET6 && (generate_random_ips|numdecoys|bouncescan|fragscan)) { fatal("Sorry -- IPv6 support is currently only available for TCP, UDP, and SCTP port scans and list scan (-sL). OS detection, random targets and decoys are also not supported with IPv6. Further support is under consideration."); } diff --git a/idle_scan.cc b/idle_scan.cc index c10e1d11f..e9cc409ad 100644 --- a/idle_scan.cc +++ b/idle_scan.cc @@ -126,7 +126,7 @@ * * ***************************************************************************/ -/* $Id$ */ +/* $Id: idle_scan.cc 2013-07-26 Mathias Morbitzer (mathias.morbitzer(at)fox-it.com) $ */ #include "idle_scan.h" #include "timing.h" @@ -169,6 +169,40 @@ struct idle_proxy_info { struct eth_nfo *ethptr; // points to eth if filled out, otherwise NULL }; +/* Finds the IPv6 extension header for fragmentation in an IPv6 packet, and returns + * the identification value of the fragmentation header +*/ +int ipv6_get_fragment_id(const struct ip6_hdr *ip6, unsigned int len) { + const unsigned char *p, *end; + u8 hdr; + struct ip6_ext_data_fragment *frag_header = NULL; + + if (len < sizeof(*ip6)) + return -1; + + p = (unsigned char *) ip6; + end = p + len; + + hdr = ip6->ip6_nxt; + p += sizeof(*ip6); + + /* If the first extension header is not the fragmentation, we search our way + * through the extension headers until we find the fragmentation header */ + while (p < end && hdr != IP_PROTO_FRAGMENT) { + if (p + 2 > end) + return -1; + hdr = *p; + p += (*(p + 1) + 1) * 8; + } + + if ( hdr != IP_PROTO_FRAGMENT || (p + 2 + sizeof(ip6_ext_data_fragment)) > end) + return -1; + + frag_header = (struct ip6_ext_data_fragment *)( p + 2 ); + + return (ntohl(frag_header->ident)); + +} /* Sends an IP ID probe to the proxy machine and returns the IP ID. This function handles retransmissions, and returns -1 if it fails. @@ -188,10 +222,18 @@ static int ipid_proxy_probe(struct idle_proxy_info *proxy, int *probes_sent, unsigned int bytes; int base_port; struct ip *ip; - struct tcp_hdr *tcp; + struct tcp_hdr *tcp = NULL; static u32 seq_base = 0; static u32 ack = 0; static int packet_send_count = 0; /* Total # of probes sent by this program -- to ensure that our sequence # always changes */ + u32 packetlen = 0; + u8 *ipv6_packet = NULL; + struct sockaddr_storage ss; + size_t sslen; + struct ip6_hdr *ip6 = NULL; + const void *ipv6_data; + u8 hdr; + int res; if (o.magic_port_set) base_port = o.magic_port; @@ -208,14 +250,30 @@ static int ipid_proxy_probe(struct idle_proxy_info *proxy, int *probes_sent, gettimeofday(&tv_sent[tries], NULL); /* Time to send the pr0be!*/ - send_tcp_raw(proxy->rawsd, proxy->ethptr, - proxy->host.v4sourceip(), proxy->host.v4hostip(), - o.ttl, false, - o.ipoptions, o.ipoptionslen, - base_port + tries, proxy->probe_port, - seq_base + (packet_send_count++ * 500) + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, - (u8 *) "\x02\x04\x05\xb4", 4, - NULL, 0); + if (o.af() == AF_INET) + send_tcp_raw(proxy->rawsd, proxy->ethptr, + proxy->host.v4sourceip(), proxy->host.v4hostip(), + o.ttl, false, + o.ipoptions, o.ipoptionslen, + base_port + tries, proxy->probe_port, + seq_base + (packet_send_count++ * 500) + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, + (u8 *) "\x02\x04\x05\xb4", 4, + NULL, 0); + else { + ipv6_packet = build_tcp_raw_ipv6(proxy->host.v6sourceip(), proxy->host.v6hostip(), + 0x00, 0x0000, + o.ttl, + base_port + tries, proxy->probe_port, + seq_base + (packet_send_count++ * 500) + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, + (u8 *) "\x02\x04\x05\xb4", 4, + NULL, 0, + &packetlen); + proxy->host.TargetSockAddr(&ss, &sslen); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send IPv6 packet"); + free(ipv6_packet); + } sent++; tries++; @@ -227,15 +285,26 @@ static int ipid_proxy_probe(struct idle_proxy_info *proxy, int *probes_sent, to_usec = proxy->host.to.timeout - TIMEVAL_SUBTRACT(tv_end, tv_sent[tries - 1]); if (to_usec < 0) to_usec = 0; // Final no-block poll - ip = (struct ip *) readipv4_pcap(proxy->pd, &bytes, to_usec, &rcvdtime, NULL, true); + ip = (struct ip *) readip_pcap(proxy->pd, &bytes, to_usec, &rcvdtime, NULL, true); gettimeofday(&tv_end, NULL); if (ip) { - if (bytes < ( 4 * ip->ip_hl) + 14U) - continue; - - if (ip->ip_p == IPPROTO_TCP) { - - tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + if (o.af() == AF_INET) { + if (bytes < ( 4 * ip->ip_hl) + 14U) + continue; + if (ip->ip_p == IPPROTO_TCP) + tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + } else if (o.af() == AF_INET6) { + if (ip->ip_v != 6) { + error("IPv6 packet with a version field != 6 received"); + } else { + ip6 = (struct ip6_hdr *) ip; + ipv6_data = ipv6_get_data(ip6, &packetlen, &hdr); + if (hdr == IPPROTO_TCP && ipv6_data != NULL) { + tcp = (struct tcp_hdr *) ipv6_data; + } + } + } + if (tcp) { if (ntohs(tcp->th_dport) < base_port || ntohs(tcp->th_dport) - base_port >= tries || ntohs(tcp->th_sport) != proxy->probe_port || ((tcp->th_flags & TH_RST) == 0)) { if (ntohs(tcp->th_dport) > o.magic_port && ntohs(tcp->th_dport) < (o.magic_port + 260)) { if (o.debugging) { @@ -245,7 +314,12 @@ static int ipid_proxy_probe(struct idle_proxy_info *proxy, int *probes_sent, proxy->host.to.rttvar = (int) (proxy->host.to.rttvar * 1.2); rcvd++; } else if (o.debugging > 1) { - error("Received unexpected response packet from %s during IP ID zombie probing:", inet_ntoa(ip->ip_src)); + char straddr[INET6_ADDRSTRLEN]; + if (o.af() == AF_INET) + inet_ntop(AF_INET, &(ip->ip_src), straddr, sizeof(straddr)); + else if (o.af() == AF_INET6) + inet_ntop(AF_INET6, &(ip6->ip6_src), straddr, sizeof(straddr)); + error("Received unexpected response packet from %s during IP ID zombie probing:", straddr); readtcppacket( (unsigned char *) ip, MIN(ntohs(ip->ip_len), bytes)); } continue; @@ -254,7 +328,10 @@ static int ipid_proxy_probe(struct idle_proxy_info *proxy, int *probes_sent, trynum = ntohs(tcp->th_dport) - base_port; rcvd++; - ipid = ntohs(ip->ip_id); + if (o.af() == AF_INET) + ipid = ntohs(ip->ip_id); + else if (o.af() == AF_INET6) + ipid = ipv6_get_fragment_id(ip6, bytes); adjust_timeouts2(&(tv_sent[trynum]), &rcvdtime, &(proxy->host.to)); } } @@ -288,6 +365,10 @@ static int ipid_distance(int seqclass , u32 startid, u32 endid) { return endid - startid; } + if (seqclass == IPID_SEQ_INCR_BY_2) { + return (endid - startid)/2; + } + return -1; } @@ -303,22 +384,139 @@ static void initialize_proxy_struct(struct idle_proxy_info *proxy) { proxy->ethptr = NULL; } +/* Forces the permanent use of the IPv6 extension header for fragmentation in each IPv6 packet sent from + * the idle host to the target or the attacker + * This is achieved by first sending a ping, and afterwards an ICMPv6 Packet Too Big message + * which states that the response from the ping was too big, our MTU is smaller than the IPv6 minimum MTU */ +static void ipv6_force_fragmentation(struct idle_proxy_info *proxy, Target *target) { + int hardtimeout = 9000000; /* Generally don't wait more than 9 secs total */ + char filter[512]; /* Libpcap filter string */ + struct ip *ip; + /* The maximum data size we can create without fragmenting, considering that the headers also need place */ + char data[IP6_MTU_MIN - IPv6_HEADER_LEN - ETH_HDR_LEN - ICMPv6_MIN_HEADER_LEN]; + unsigned int datalen, bytes; + const unsigned int proxy_reply_timeout = 2000; + const void *rdata; //the data received in the echo response + struct timeval tmptv, rcvdtime, ipv6_packet_send_time; + struct abstract_ip_hdr hdr; + bool response_received = false; + struct icmpv6_hdr *icmp6_header; + u8 *ipv6_packet = NULL; + u32 packetlen = 0; + u16 pingid = 0; + u16 seq = 0; + struct sockaddr_storage ss; + size_t sslen; + int res; + assert(proxy); + + /* First, we force the proxy to provide us with a fragmentation header in each packet + by sending an ping and afterwards an ICMPv6 Packet Too Big */ + memset(data,'A', sizeof(data)); + pingid = get_random_u16(); + seq = get_random_u16(); + + /* pcap, to get the answer. Max size here is the IPv6 minimum MTU */ + if ((proxy->pd = my_pcap_open_live(proxy->host.deviceName(), IP6_MTU_MIN, (o.spoofsource) ? 1 : 0, 50)) == NULL) + fatal("%s", PCAP_OPEN_ERRMSG); + + Snprintf(filter, sizeof(filter), "icmp6 and src host %s and dst host %s", proxy->host.targetipstr(), proxy->host.sourceipstr()); + if (o.debugging) + log_write(LOG_STDOUT, "Packet capture filter (device %s): %s\n", proxy->host.deviceFullName(), filter); + + /* Make a ping that is in total 1280 byte long and send it */ + proxy->host.TargetSockAddr(&ss, &sslen); + ipv6_packet = build_icmpv6_raw(proxy->host.v6sourceip(), proxy->host.v6hostip(), 0x00, 0x0000, o.ttl, seq , pingid, ICMPV6_ECHO, 0x00, data, sizeof(data) , &packetlen); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send ICMPv6 Echo Request to the idle host"); + free(ipv6_packet); + gettimeofday(&ipv6_packet_send_time, NULL); + + /* Now let's wait for the answer */ + while (!response_received) { + gettimeofday(&tmptv, NULL); + ip = (struct ip *) readip_pcap(proxy->pd, &bytes, proxy_reply_timeout, &rcvdtime, NULL, true); + if (!ip) { + if (TIMEVAL_SUBTRACT(tmptv, ipv6_packet_send_time) >= hardtimeout) { + fatal("Idle scan zombie %s (%s) port %hu cannot be used because it has not returned any of our ICMPv6 Echo Requests -- perhaps it is down or firewalled.", + proxy->host.HostName(), proxy->host.targetipstr(), + proxy->probe_port); + } + continue; + } + datalen = bytes; + rdata = ip_get_data(ip, &datalen, &hdr); + if (hdr.version == 6 && hdr.proto == IPPROTO_ICMPV6) { + icmp6_header = (struct icmpv6_hdr *) rdata; + if (icmp6_header->icmpv6_type == ICMPV6_ECHOREPLY) { + const struct icmpv6_msg_echo *echo; + echo = (struct icmpv6_msg_echo *) ((u8 *) icmp6_header + sizeof(*icmp6_header)); + if (ntohs(echo->icmpv6_id) == pingid && ntohs(echo->icmpv6_seq) == seq) + response_received=true; + } + } + } + + if (proxy->pd) + pcap_close(proxy->pd); + + /* Now we can tell the idle host that its reply was too big, we want it smaller than the IPV6 minimum MTU */ + /* the data contains first the MTU we want, and then the received IPv6 package */ + *(uint32_t *)&data = ntohl(IP6_MTU_MIN - 2); + memcpy(&data[4], ip, sizeof(data)-4); + + ipv6_packet = build_icmpv6_raw(proxy->host.v6sourceip(), proxy->host.v6hostip(), 0x00, 0x0000, o.ttl, 0x00 , 0x00, 0x02, 0x00, data, sizeof(data) , &packetlen); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send spoofed ICMPv6 Echo Request to the idle host"); + + free(ipv6_packet); + + /* Now we do the same in the name of the target */ + /* No pcap this time, we won't receive the answer */ + memset(data,'A', sizeof(data)); + pingid = get_random_u16(); + seq = get_random_u16(); + + ipv6_packet = build_icmpv6_raw(target->v6hostip(), proxy->host.v6hostip(), 0x00, 0x0000, o.ttl, seq , pingid, ICMPV6_ECHO, 0x00, data, sizeof(data) , &packetlen); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send ICMPv6 Echo Request to the idle host"); + + free(ipv6_packet); + + /* Now we guess what answer the decoy host sent to the target, so that we can piggyback this on the ICMPV6 Packet too Big message */ + ipv6_packet = build_icmpv6_raw(proxy->host.v6hostip(), target->v6hostip(), 0x00, 0x0000, o.ttl, seq , pingid, ICMPV6_ECHOREPLY, 0x00, data, sizeof(data) , &packetlen); + *(uint32_t *)&data = ntohl(IP6_MTU_MIN - 2); + memcpy(&data[4], ipv6_packet, sizeof(data)-4); + free(ipv6_packet); + + ipv6_packet = build_icmpv6_raw(target->v6hostip(), proxy->host.v6hostip(), 0x00, 0x0000, o.ttl, 0x00 , 0x00, 0x02, 0x00, data, sizeof(data) , &packetlen); + /* give the decoy host time to reply to the target */ + usleep(10000); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send ICMPv6 PTB to the idle host"); + free(ipv6_packet); +} + /* takes a proxy name/IP, resolves it if necessary, tests it for IP ID suitability, and fills out an idle_proxy_info structure. If the proxy is determined to be unsuitable, the function whines and exits the program */ #define NUM_IPID_PROBES 6 static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, - const struct in_addr *first_target, const struct scan_lists *ports) { - int probes_sent = 0, probes_returned = 0; + Target *target, const struct scan_lists *ports) { + unsigned int probes_sent = 0, probes_returned = 0; int hardtimeout = 9000000; /* Generally don't wait more than 9 secs total */ unsigned int bytes, to_usec; int timedout = 0; - char *p, *q; + char *p, *q = NULL, *r; char *endptr = NULL; int seq_response_num; int newipid; - int i; + unsigned int i; char filter[512]; /* Libpcap filter string */ char name[MAXHOSTNAMELEN + 1]; struct sockaddr_storage ss; @@ -333,8 +531,16 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, int ipids[NUM_IPID_PROBES]; u8 probe_returned[NUM_IPID_PROBES]; struct route_nfo rnfo; + assert(proxyName); + u8 *ipv6_packet = NULL; + u32 packetlen = 0; + const struct ip6_hdr *ip6; + u8 ip6hdr; + const void *ip6data; + bool retried_forcing_fragmentation = false; assert(proxy); assert(proxyName); + int res; ack = get_random_u32(); @@ -348,8 +554,26 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, proxy->max_groupsz = MAX(proxy->min_groupsz, o.max_parallelism ? o.max_parallelism : 100); proxy->max_senddelay = 100000; - Strncpy(name, proxyName, sizeof(name)); - q = strchr(name, ':'); + + /* If we have an IPv6 address, we specify the port with [address]:port */ + if (o.af() == AF_INET) + q = strchr(proxyName, ':'); + else if (o.af() == AF_INET6) { + r = strstr(proxyName, "]:"); + if (r != NULL) + q = strchr(r, ':'); + else + q = NULL; + } + + /* If we have a : in IPv4 or [] in IPv6, we strip them off */ + if (o.af() == AF_INET && strchr(proxyName, ':') != NULL ) + strncpy(name, proxyName , MIN(strcspn(proxyName,":") , sizeof(name))); + else if (o.af() == AF_INET6 && strchr(proxyName, '[') != NULL && strchr(proxyName, ']') != NULL) + strncpy(name, strchr(proxyName, '[') + 1, MIN(strcspn(proxyName,"]") - strcspn(proxyName, "[") - 1, sizeof(name))); + else + strncpy(name, proxyName, sizeof(name)); + if (q) { *q++ = '\0'; proxy->probe_port = strtoul(q, &endptr, 10); @@ -427,18 +651,21 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, proxy->ethptr = NULL; } - /* Now for the pcap opening nonsense ... */ - /* Note that the snaplen is 152 = 64 byte max IPhdr + 24 byte max link_layer - * header + 64 byte max TCP header. */ - if ((proxy->pd = my_pcap_open_live(proxy->host.deviceName(), 152, (o.spoofsource) ? 1 : 0, 50)) == NULL) + if (proxy->host.af() == AF_INET6) + ipv6_force_fragmentation(proxy, target); + + /* Now for the pcap opening nonsense ... + Snaplen will be the IPv6 minimum MTU of 1280, because an IPv6 packet + may have any number of extension header up to the minimal IPv6 MTU */ + if ((proxy->pd = my_pcap_open_live(proxy->host.deviceName(), IP6_MTU_MIN, (o.spoofsource) ? 1 : 0, 50)) == NULL) fatal("%s", PCAP_OPEN_ERRMSG); - p = strdup(proxy->host.targetipstr()); - q = strdup(inet_ntoa(proxy->host.v4source())); - Snprintf(filter, sizeof(filter), "tcp and src host %s and dst host %s and src port %hu", p, q, proxy->probe_port); - free(p); - free(q); + p = (char *) proxy->host.targetipstr(); + q = (char *) proxy->host.sourceipstr(); + + /* libpcap doesn't find the source port in IPv6 if there is an extension header. So we check for this later in the tcp header. */ + Snprintf(filter, sizeof(filter), "tcp and src host %s and dst host %s", p, q); set_pcap_filter(proxy->host.deviceFullName(), proxy->pd, filter); if (o.debugging) log_write(LOG_STDOUT, "Packet capture filter (device %s): %s\n", proxy->host.deviceFullName(), filter); @@ -461,14 +688,30 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, a response with the exact request for timing purposes. So I think I'll use TH_SYN, although it is a tough call. */ /* We can't use decoys 'cause that would screw up the IP IDs */ - send_tcp_raw(proxy->rawsd, proxy->ethptr, - proxy->host.v4sourceip(), proxy->host.v4hostip(), - o.ttl, false, - o.ipoptions, o.ipoptionslen, - o.magic_port + probes_sent + 1, proxy->probe_port, - sequence_base + probes_sent + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, - (u8 *) "\x02\x04\x05\xb4", 4, - NULL, 0); + if (o.af() == AF_INET) + send_tcp_raw(proxy->rawsd, proxy->ethptr, + proxy->host.v4sourceip(), proxy->host.v4hostip(), + o.ttl, false, + o.ipoptions, o.ipoptionslen, + o.magic_port + probes_sent + 1, proxy->probe_port, + sequence_base + probes_sent + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, + (u8 *) "\x02\x04\x05\xb4", 4, + NULL, 0); + else if (o.af() == AF_INET6) { + ipv6_packet = build_tcp_raw_ipv6(proxy->host.v6sourceip(), proxy->host.v6hostip(), + 0x00, 0x0000, + o.ttl, + o.magic_port + probes_sent + 1, proxy->probe_port, + sequence_base + probes_sent + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, + (u8 *) "\x02\x04\x05\xb4", 4, + NULL, 0, + &packetlen); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send IPv6 packet"); + free(ipv6_packet); + } + gettimeofday(&probe_send_times[probes_sent], NULL); probes_sent++; @@ -476,7 +719,7 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, while (probes_returned < probes_sent && !timedout) { to_usec = (probes_sent == NUM_IPID_PROBES) ? hardtimeout : 1000; - ip = (struct ip *) readipv4_pcap(proxy->pd, &bytes, to_usec, &rcvdtime, NULL, true); + ip = (struct ip *) readip_pcap(proxy->pd, &bytes, to_usec, &rcvdtime, NULL, true); gettimeofday(&tmptv, NULL); @@ -491,32 +734,94 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, timedout = 1; } - if (lastipid != 0 && ip->ip_id == lastipid) { - continue; /* probably a duplicate */ - } - lastipid = ip->ip_id; - - if (bytes < ( 4 * ip->ip_hl) + 14U) - continue; - - if (ip->ip_p == IPPROTO_TCP) { - tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); - if (ntohs(tcp->th_dport) < (o.magic_port + 1) || ntohs(tcp->th_dport) - o.magic_port > NUM_IPID_PROBES || ntohs(tcp->th_sport) != proxy->probe_port || ((tcp->th_flags & TH_RST) == 0)) { - if (o.debugging > 1) - error("Received unexpected response packet from %s during initial IP ID zombie testing", inet_ntoa(ip->ip_src)); + if (o.af() == AF_INET) { + if (ip->ip_v != 4) { + error("Received a packet with version field != 4"); continue; } + if (lastipid != 0 && ip->ip_id == lastipid) { + continue; /* probably a duplicate */ + } + lastipid = ip->ip_id; + if (bytes < ( 4 * ip->ip_hl) + 14U) + continue; - seq_response_num = probes_returned; + if (ip->ip_p == IPPROTO_TCP) { + tcp = ((struct tcp_hdr *) (((char *) ip) + 4 * ip->ip_hl)); + /* Checking now for the source port, which we were not able to do in the libpcap filter */ + if (ntohs(tcp->th_sport) != proxy->probe_port) { + continue; + } - /* The stuff below only works when we send SYN packets instead of - SYN|ACK, but then are slightly less stealthy and have less chance - of sneaking through the firewall. Plus SYN|ACK is what they will - be receiving back from the target */ - probes_returned++; - ipids[seq_response_num] = ntohs(ip->ip_id); - probe_returned[seq_response_num] = 1; - adjust_timeouts2(&probe_send_times[seq_response_num], &rcvdtime, &(proxy->host.to)); + if (ntohs(tcp->th_dport) < (o.magic_port + 1) || ntohs(tcp->th_dport) - o.magic_port > NUM_IPID_PROBES || ((tcp->th_flags & TH_RST) == 0)) { + if (o.debugging > 1) + error("Received unexpected response packet from %s during initial IP ID zombie testing", inet_ntoa(ip->ip_src)); + continue; + } + + seq_response_num = probes_returned; + + /* The stuff below only works when we send SYN packets instead of + SYN|ACK, but then are slightly less stealthy and have less chance + of sneaking through the firewall. Plus SYN|ACK is what they will + be receiving back from the target */ + probes_returned++; + ipids[seq_response_num] = ntohs(ip->ip_id); + probe_returned[seq_response_num] = 1; + adjust_timeouts2(&probe_send_times[seq_response_num], &rcvdtime, &(proxy->host.to)); + } + } else if (o.af() == AF_INET6) { + if (ip->ip_v != 6) { + error("Received a packet with version field != 6"); + continue; + } else { + ip6 = (struct ip6_hdr *) ip; + newipid = ipv6_get_fragment_id(ip6, bytes); + if (newipid < 0 ) { + /* ok, the idle host does not seem to append the extension header for fragmentation. Let's try this once more, + * maybe the idle host just adjusted its Path MTU. If we keep on having the problem, we quit */ + if (!retried_forcing_fragmentation) { + ipv6_force_fragmentation(proxy, target); + retried_forcing_fragmentation = true; + } else + fatal("IPv6 packet without fragmentation header received - issues with the zombie?"); + } + /* now that the additional ipv6 stuff is done, we do as for IPv4 */ + if (lastipid != 0 && newipid == (int)lastipid) { + continue; /* probably a duplicate */ + } + lastipid = newipid; + + ip6data = ipv6_get_data(ip6, &packetlen, &ip6hdr); + if (ip6hdr == IPPROTO_TCP && ip6data != NULL) { + tcp = (struct tcp_hdr *) ip6data; + /* Checking now for the source port, which we were not able to do in the libpcap filter */ + if (ntohs(tcp->th_sport) != proxy->probe_port) { + continue; + } + }else + { + error("Malformed packet received"); + continue; + } + + if (ntohs(tcp->th_dport) < (o.magic_port + 1) || ntohs(tcp->th_dport) - o.magic_port > NUM_IPID_PROBES || ((tcp->th_flags & TH_RST) == 0)) { + if (o.debugging > 1) + error("Received unexpected response packet from %s during initial IP ID zombie testing", inet_ntoa(ip->ip_src)); + continue; + } + + seq_response_num = probes_returned; + + /* The stuff below only works when we send SYN packets instead of + SYN|ACK, but then are slightly less stealthy and have less chance + of sneaking through the firewall. Plus SYN|ACK is what they will + be receiving back from the target */ + probes_returned++; + ipids[seq_response_num] = newipid; + probe_returned[seq_response_num] = 1; + adjust_timeouts2(&probe_send_times[seq_response_num], &rcvdtime, &(proxy->host.to)); + } } } } @@ -535,9 +840,13 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, proxy->host.HostName(), proxy->host.targetipstr(), proxy->probe_port); - proxy->seqclass = get_ipid_sequence(probes_returned, ipids, 0); + if (o.af() == AF_INET) + proxy->seqclass = get_ipid_sequence_16(probes_returned, ipids, 0); + else + proxy->seqclass = get_ipid_sequence_32(probes_returned, ipids, 0); switch (proxy->seqclass) { case IPID_SEQ_INCR: + case IPID_SEQ_INCR_BY_2: case IPID_SEQ_BROKEN_INCR: log_write(LOG_PLAIN, "Idle scan using zombie %s (%s:%hu); Class: %s\n", proxy->host.HostName(), proxy->host.targetipstr(), proxy->probe_port, ipidclass2ascii(proxy->seqclass)); break; @@ -568,19 +877,34 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, changed. This will also catch the case where the Nmap user is behind an egress filter or other measure that prevents this sort of sp00fery */ - if (first_target) { + /* this behavior gets quite common in IPv6 so now its even more important to check */ + if (target->v4hostip() || target->v6hostip()) { for (probes_sent = 0; probes_sent < 4; probes_sent++) { if (probes_sent != 0) usleep(50000); - send_tcp_raw(proxy->rawsd, proxy->ethptr, - first_target, proxy->host.v4hostip(), - o.ttl, false, - o.ipoptions, o.ipoptionslen, - o.magic_port, proxy->probe_port, - sequence_base + probes_sent + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, - (u8 *) "\x02\x04\x05\xb4", - 4, NULL, 0); - + if (target->v4hostip()) { + send_tcp_raw(proxy->rawsd, proxy->ethptr, + target->v4hostip(), proxy->host.v4hostip(), + o.ttl, false, + o.ipoptions, o.ipoptionslen, + o.magic_port, proxy->probe_port, + sequence_base + probes_sent + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, + (u8 *) "\x02\x04\x05\xb4", + 4, NULL, 0); + } else { + ipv6_packet = build_tcp_raw_ipv6(target->v6hostip(), proxy->host.v6hostip(), + 0x00, 0x0000, + o.ttl, + o.magic_port, proxy->probe_port, + sequence_base + probes_sent + 1, ack, 0, TH_SYN | TH_ACK, 0, 0, + (u8 *) "\x02\x04\x05\xb4", + 4, NULL, 0, + &packetlen); + res = send_ip_packet(proxy->rawsd, proxy->ethptr, &ss, ipv6_packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send IPv6 packet "); + free(ipv6_packet); + } } /* Sleep a little while to give packets time to reach their destination */ @@ -695,10 +1019,16 @@ static int idlescan_countopen2(struct idle_proxy_info *proxy, int lasttry = 0; int dotry3 = 0; struct eth_nfo eth; + u8 *packet = NULL; + struct sockaddr_storage ss; + size_t sslen; + u32 packetlen = 0; + int res; if (seq == 0) seq = get_random_u32(); + target->TargetSockAddr(&ss, &sslen); memset(&end, 0, sizeof(end)); memset(&latestchange, 0, sizeof(latestchange)); gettimeofday(&start, NULL); @@ -727,13 +1057,27 @@ static int idlescan_countopen2(struct idle_proxy_info *proxy, but doing it the straightforward way (using the same decoys as we use in probing the proxy box is risky. I'll have to think about this more. */ - send_tcp_raw(proxy->rawsd, eth.ethsd ? ð : NULL, - proxy->host.v4hostip(), target->v4hostip(), - o.ttl, false, - o.ipoptions, o.ipoptionslen, - proxy->probe_port, ports[pr0be], seq, 0, 0, TH_SYN, 0, 0, - (u8 *) "\x02\x04\x05\xb4", 4, - o.extra_payload, o.extra_payload_length); + if ( o.af() == AF_INET ) { + send_tcp_raw(proxy->rawsd, eth.ethsd ? ð : NULL, + proxy->host.v4hostip(), target->v4hostip(), + o.ttl, false, + o.ipoptions, o.ipoptionslen, + proxy->probe_port, ports[pr0be], seq, 0, 0, TH_SYN, 0, 0, + (u8 *) "\x02\x04\x05\xb4", 4, + o.extra_payload, o.extra_payload_length); + } else { + packet = build_tcp_raw_ipv6(proxy->host.v6hostip(), target->v6hostip(), + 0x00, 0x0000, + o.ttl, + proxy->probe_port, ports[pr0be], seq, 0, 0, TH_SYN, 0, 0, + (u8 *) "\x02\x04\x05\xb4", 4, + o.extra_payload, o.extra_payload_length, + &packetlen); + res = send_ip_packet(proxy->rawsd, eth.ethsd ? ð : NULL, &ss, packet, packetlen); + if (res == -1) + fatal("Error occured while trying to send IPv6 packet"); + free(packet); + } } gettimeofday(&end, NULL); @@ -1044,7 +1388,7 @@ void idle_scan(Target *target, u16 *portarray, int numports, /* If this is the first call, */ if (!*lastproxy) { - initialize_idleproxy(&proxy, proxyName, target->v4hostip(), ports); + initialize_idleproxy(&proxy, proxyName, target, ports); strncpy(lastproxy, proxyName, sizeof(lastproxy)); } diff --git a/nmap.cc b/nmap.cc index 71f80b9a3..a5a7913ea 100644 --- a/nmap.cc +++ b/nmap.cc @@ -2689,6 +2689,8 @@ const char *ipidclass2ascii(int seqclass) { return "Duplicated ipid (!)"; case IPID_SEQ_INCR: return "Incremental"; + case IPID_SEQ_INCR_BY_2: + return "Incrementing by 2"; case IPID_SEQ_BROKEN_INCR: return "Broken little-endian incremental"; case IPID_SEQ_RD: diff --git a/nmap.h b/nmap.h index 23d64eba2..b0185e169 100644 --- a/nmap.h +++ b/nmap.h @@ -410,6 +410,7 @@ void *realloc(); #define IPID_SEQ_RD 4 /* Appears to select IPID using a "random" distributions (meaning it can go up or down) */ #define IPID_SEQ_CONSTANT 5 /* Contains 1 or more sequential duplicates */ #define IPID_SEQ_ZERO 6 /* Every packet that comes back has an IP.ID of 0 (eg Linux 2.4 does this) */ +#define IPID_SEQ_INCR_BY_2 7 /* simple increment by two each time */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 diff --git a/osscan2.cc b/osscan2.cc index 65b88a5f7..eed70db70 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -198,6 +198,7 @@ static struct AVal *make_aval_ipid_seq(struct AVal *av, const char *attribute, case IPID_SEQ_CONSTANT: av->value = string_pool_sprintf("%X", ipids[0]); break; + case IPID_SEQ_INCR_BY_2: case IPID_SEQ_INCR: av->value = "I"; break; @@ -247,32 +248,9 @@ int get_initial_ttl_guess(u8 ttl) { one of the IPID_SEQ_* classifications defined in nmap.h . If the function cannot determine the sequence, IPID_SEQ_UNKNOWN is returned. This islocalhost argument is a boolean specifying whether these - numbers were generated by scanning localhost. NOTE: the "ipids" argument - may be modified if localhost is set to true. */ -int get_ipid_sequence(int numSamples, int *ipids, int islocalhost) { - u16 ipid_diffs[32]; - int i, j, k; - int allipideqz = 1; /* Flag that means "All IP.IDs returned during sequencing - * are zero. This is unset if we find a nonzero */ - - assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); - if (numSamples < 2) - return IPID_SEQ_UNKNOWN; - - for (i = 1; i < numSamples; i++) { - if (ipids[i - 1] != 0 || ipids[i] != 0) - allipideqz = 0; /* All IP.ID values do *NOT* equal zero */ - - if (ipids[i - 1] <= ipids[i]) { - ipid_diffs[i - 1] = ipids[i] - ipids[i - 1]; - } else { - ipid_diffs[i - 1] = (u16) (ipids[i] - ipids[i - 1] + 65536); - } - - /* Random */ - if (numSamples > 2 && ipid_diffs[i - 1] > 20000) - return IPID_SEQ_RD; - } + numbers were generated by scanning localhost. */ +int identify_sequence(int numSamples, u32 *ipid_diffs, int islocalhost, int allipideqz) { + int i, j, k, l; /* ZERO */ if (allipideqz) @@ -321,11 +299,16 @@ int get_ipid_sequence(int numSamples, int *ipids, int islocalhost) { j = 1; /* j is a flag meaning "all differences seen are < 10" */ k = 1; /* k is a flag meaning "all difference seen are multiples of 256 and * no greater than 5120" */ + l = 1; /* l is a flag meaning "all differences are multiples of 2" */ for (i = 0; i < numSamples - 1; i++) { if (k && (ipid_diffs[i] > 5120 || ipid_diffs[i] % 256 != 0)) { k = 0; } + if (l && ipid_diffs[i] % 2 != 0) { + l = 0; + } + if (j && ipid_diffs[i] > 9) { j = 0; } @@ -336,13 +319,69 @@ int get_ipid_sequence(int numSamples, int *ipids, int islocalhost) { return IPID_SEQ_BROKEN_INCR; } - /* Incremental */ + /* Incrementing by 2 */ + if (l == 1) + return IPID_SEQ_INCR_BY_2; + + /* Incremental by 1 */ if (j == 1) return IPID_SEQ_INCR; return IPID_SEQ_UNKNOWN; } +/* Calculate the distances between the ipids and write them + into the ipid_diffs array */ +int get_diffs(u32 *ipid_diffs, int numSamples, int *ipids, int islocalhost) { + int i; + int allipideqz = 1; + + if (numSamples < 2) + return IPID_SEQ_UNKNOWN; + + for (i = 1; i < numSamples; i++) { + if (ipids[i - 1] != 0 || ipids[i] != 0) + allipideqz = 0; /* All IP.ID values do *NOT* equal zero */ + + if (ipids[i - 1] <= ipids[i]) { + ipid_diffs[i - 1] = ipids[i] - ipids[i - 1]; + } else { + ipid_diffs[i - 1] = (u32) (ipids[i] - ipids[i - 1] + 4294967296); + } + + /* Random */ + if (numSamples > 2 && ipid_diffs[i - 1] > 20000) + return IPID_SEQ_RD; + } + + return allipideqz; + +} + +/* Indentify the ipid sequence for 32-bit IPID values (IPv6) */ +int get_ipid_sequence_32(int numSamples, int *ipids, int islocalhost) { + int allipideqz=1; + u32 ipid_diffs[32]; + assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); + allipideqz = get_diffs(ipid_diffs, numSamples, ipids, islocalhost); + return identify_sequence(numSamples, ipid_diffs, islocalhost, allipideqz); +} + +/* Indentify the ipid sequence for 16-bit IPID values (IPv4) */ +int get_ipid_sequence_16(int numSamples, int *ipids, int islocalhost) { + int i; + int allipideqz=1; + u32 ipid_diffs[32]; + assert(numSamples < (int) (sizeof(ipid_diffs) / 2)); + allipideqz = get_diffs(ipid_diffs, numSamples, ipids, islocalhost); + /* AND with 0xffff so that in case the 16 bit counter was + * flipped over we still have a continuous sequence */ + for (i = 0; i < numSamples; i++) { + ipid_diffs[i] = ipid_diffs[i] & 0xffff; + } + return identify_sequence(numSamples, ipid_diffs, islocalhost, allipideqz); +} + /* Start the timeout clocks of any targets that aren't already timedout */ static void startTimeOutClocks(OsScanInfo *OSI) { @@ -2374,7 +2413,7 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { } if (good_tcp_ipid_num >= 3) { - tcp_ipid_seqclass = get_ipid_sequence(good_tcp_ipid_num, hss->ipid.tcp_ipids, islocalhost(hss->target->TargetSockAddr())); + tcp_ipid_seqclass = get_ipid_sequence_16(good_tcp_ipid_num, hss->ipid.tcp_ipids, islocalhost(hss->target->TargetSockAddr())); } else { tcp_ipid_seqclass = IPID_SEQ_UNKNOWN; } @@ -2382,13 +2421,13 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) { hss->si.ipid_seqclass = tcp_ipid_seqclass; if (good_tcp_closed_ipid_num >= 2) { - tcp_closed_ipid_seqclass = get_ipid_sequence(good_tcp_closed_ipid_num, hss->ipid.tcp_closed_ipids, islocalhost(hss->target->TargetSockAddr())); + tcp_closed_ipid_seqclass = get_ipid_sequence_16(good_tcp_closed_ipid_num, hss->ipid.tcp_closed_ipids, islocalhost(hss->target->TargetSockAddr())); } else { tcp_closed_ipid_seqclass = IPID_SEQ_UNKNOWN; } if (good_icmp_ipid_num >= 2) { - icmp_ipid_seqclass = get_ipid_sequence(good_icmp_ipid_num, hss->ipid.icmp_ipids, islocalhost(hss->target->TargetSockAddr())); + icmp_ipid_seqclass = get_ipid_sequence_16(good_icmp_ipid_num, hss->ipid.icmp_ipids, islocalhost(hss->target->TargetSockAddr())); } else { icmp_ipid_seqclass = IPID_SEQ_UNKNOWN; } diff --git a/osscan2.h b/osscan2.h index d55b96b98..b30a5af7e 100644 --- a/osscan2.h +++ b/osscan2.h @@ -187,8 +187,11 @@ typedef enum OFProbeType { void os_scan2(std::vector &Targets); int get_initial_ttl_guess(u8 ttl); -int get_ipid_sequence(int numSamples, int *ipids, int islocalhost); +int identify_sequence(int numSamples, u32 *ipid_diffs, int islocalhost, int allipideqz); +int get_diffs(u32 *ipid_diffs, int numSamples, int *ipids, int islocalhost); +int get_ipid_sequence_16(int numSamples, int *ipids, int islocalhost); +int get_ipid_sequence_32(int numSamples, int *ipids, int islocalhost); /****************************************************************************** * CLASS DEFINITIONS *