1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00

IPv6 idle scan patch by Mathias Morbitzer.

http://seclists.org/nmap-dev/2013/q3/549
This commit is contained in:
david
2013-10-27 06:33:49 +00:00
parent d5f61304c0
commit 36bea16af7
6 changed files with 505 additions and 116 deletions

View File

@@ -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.");
}

View File

@@ -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 ? &eth : 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 ? &eth : 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 ? &eth : 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));
}

View File

@@ -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:

1
nmap.h
View File

@@ -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

View File

@@ -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;
}

View File

@@ -187,8 +187,11 @@ typedef enum OFProbeType {
void os_scan2(std::vector<Target *> &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 *