ICMP                                                                                                                                                                                                                                         

____________________________________________________________________

 

ICMP or The Internet Control Message Protocol is the de facto protocol used to communicate error messages reporting errors that might have occurred while transferring data over networks. Besides, the normal use of reporting error messages, ICMP also plays an extremely important role in gathering information about the remote system. This manual explores everything about ICMP, its uses and how to use ICMP to gather more information on the target system and of course, countermeasures.

The ICMP protocol communicates error messages and other malfunctions or problems that might have occurred while the data transactions between two systems were taking place. Thus, it can also be called the ‘Network Problem Diagnosis’ protocol. It is basically the protocol, which reports any error that might have occurred while the data transfer was still taking place.

All ICMP messages are transmitted as IP datagrams. A Typical ICMP message encapsulated within an IP datagram would be as follows:

_______________________________________________________

|                                               |                                                                              |               

|               IP Header              |               ICMP Message                                    |                                                                               

|                                               |                                                                              |               

|____________________|_________________________________ |

              20 Bytes

       

|-------------------------------IP Datagram-----------------------------------|

 

The first 4 bytes have the same format and specification for all the messages; however, the remaining part of the datagram differs from message to message depending upon the kind or message i.e. the type of error message carried by the datagram. This means that the first 4 bytes are common to all ICMP messages, while the remaining bytes are different for different error messages, depending upon the type of error transmitted by that particular message. 

The format of a typical ICMP message is as follows:

 

0                         7    8                                     15      16                                                             31

____________________________________________________________________

|                               |                                               |                                                                            | 

|     8-Bit Type        |               8-bit Code              |               16-Bit Checksum                               |

|                               |                                               |                                                                            |

|_____________ |____________________ |_________________________________|

|                                                                                                                                                            |

|                                                                                                                                                            |

|                                                                                                                                                            |

|                               (Contents depending upon Type and Code)                                               |

|                                                                                                                                                            | 

|                                                                                                                                                            | 

|                                                                                                                                                            |

|___________________________________________________________________|

 

The ‘type’ field can have any of the 15 different values, which determine or represent a particular ICMP error message. For Example, a value of 3 in the type field specifies the ‘Destination unreachable’ error message. Like this there are 15 different values, which the ‘type’ field can have, with each value representing a particular specific error type.  

An error message like the above was a very general or rather a very broad error message. It does not tell you the exact cause of the error or where exactly or what exactly caused the error to occur. Thus, it is not that useful for diagnose purposes. In order to ensure that the user or application running on either the client side or the server side knows about the error occurred in a more detailed fashion, ICMP has the provision whereby, each main error specified by the ‘type’ value has a number of sub errors, which give a more specific reason or cause of the error.  

Such sub-errors, which are more specific and more helpful, are specified in ICMP in the ‘code’ field.

For Example, a ‘Type’ Value of 3 and a ‘code’ value of 0 specify that the error caused is: ‘Network Unreachable’. While a ‘Type’ Value of 3 and a ‘code’ value of 1 specifies that the error caused is: ‘Host Unreachable’ 

The following table gives the complete list of type values, code values and their corresponding errors, which are allowed by ICMP: 

Type

Name

Code

0

Echo Reply

0 No Code

1

Unassigned

 

2

Unassigned

 

3

Destination Unreachable:

0 Net Unreachable
1 Host Unreachable
2 Protocol Unreachable
3 Port Unreachable
4 Fragmentation Needed and Don't Fragment was Set
5 Source Route Failed
6 Destination Network Unknown
7 Destination Host Unknown
8 Source Host Isolated
9 Communication with Destination
Network is Administratively Prohibited
10 Communication with Destination Host is
Administratively Prohibited6
11 Destination Network Unreachable for Type of
Service.
12 Destination Host Unreachable
for Type of Service.
13 Communication Administratively Prohibited.
14 Host Precedence Violation

15 Precedence cutoff in effect

4

Source Quench
0 No Code

5

Redirect

 

 

 

0 Redirect Datagram for the Network (or subnet)

 

 

1 Redirect Datagram for the Host

 

 

2 Redirect Datagram for the Type of Service and
Network

 

 

3 Redirect Datagram for the Type of Service and
Host

6

Alternate Host Address
0 Alternate Address for Host

7

Unassigned

 

8

Echo Request
0 No Code

9

Router Advertisement
0 No Code

10

Router Selection
0 No Code

11

Time Exceeded

 

 

 

0 Time to Live exceeded in Transit

 

 

1 Fragment Reassembly Time Exceeded

12

Parameter Problem

 

 

 

0 Pointer indicates the error

 

 

1 Missing a Required Option

 

 

2 Bad Length

13

Timestamp
0 No Code

14

Timestamp Reply
0 No Code

15

Information Request
0 No Code (Obsolete)

16

Information Reply
0 No Code (Obsolete)

17

Address Mask Request
0 No Code

18

Address Mask Reply
0 No Code

 

As soon as TCP/IP encounters an error in the data transfer from the source to the destination, ICMP is used to inform the client, server and the user process about the details of the error encountered.  Thus, the prime usage of ICMP is in relaying error occurred in the data transit.  

Remote Host Information Gathering with ICMP

 

The various ICMP messages having different code and type values can be used to get different kinds of information on the remote system. Information gathering is the first step that a hacker goes through in his attempt to detect any loopholes in the target system. Although sending ICMP messages will not tell you everything that you will need to know about the target system, but still they will give you quite a lot of information. 

Using the Echo Request and Echo Reply Messages 

The Echo request message is the ICMP message with a type value of 8 and a code value of 0, while the Echo reply message is the ICMP message with a type value of 0 and a code value of 0. We can use the ICMP Echo Request and Echo Reply messages to determine whether or not the target host is connected to the Internet (is alive) or not. 

In order to determine whether the target host is alive or not, all we have to do is send an Echo Request message to the remote system and wait for an Echo reply message. 

  1. If the remote system replies with an Echo Reply message, then it means that it is alive and is connected to the Internet.
  2. However, if we do not receive any reply from the remote system, then it probably means that the remote system is not connected to the Internet.

 

However, one note of caution, it is more than possibility that the target host to whom you send out the Echo request datagram might have a filtering device installed which discards all Echo Request ICMP messages, thus showing that the remote system is not connected to the Internet even though it might as well be connected. Some better firewalls even send out spoofed Echo Replies on encountering such echo requests, thus throwing the malicious intruders off the track.

 

***********************
HACKING TRUTH: Does the working of the Echo Request and Echo Reply Messages sound familiar? You guessed it, they are indeed same as the popular Unix network diagnosing command: ping.

**********************

How exactly do the ICMP Echo Request and Reply Messages work? To understand their working, let us first take a look at their structures: 

The format of a typical ICMP ‘echo request’ and ‘echo reply’ message would be as follows: 

0                         7    8                                     15      16                                                                 31

____________________________________________________________________

|                               |                                               |                                                                             |

|     Type =0or 8     |               Code =0                 |               16-Bit Checksum                                |

|                               |                                               |                                                                             |

|_____________ |____________________ |________________________________  | 

|                                                                               |                                                                             |

|               Identifier                                                |               sequence number                               |

|__________________________________|_________________________________|

|                                                                                                                                                            |

|                                                                                                                                                            | 

|                                                             (Optional Data)                                                                    | 

|                                                                                                                                                            |

|___________________________________________________________________|

 The sender initializes an ICMP echo request message giving the Identifier field the value of the Process ID of the sending process, the sequence number field a sequence number and adding some binary data to the data field. The target system then on receiving the request, simply echoes back the identifier, data (unchanged) and sequence number fields in the form of an ICMP reply message. When the sender receives this message, it can deduce that the target system is alive. 

This method can easily be carried out using the ping utility. For Example, 

# ping hackingtruths.box.sk 

Pinging hackingtruths.box.sk [194.x.yyy.227] with 32 bytes of data:

32 bytes from 194.x.yyy.227: icmp_seq=0 ttl=225 time=0 ms

32 bytes from 194.x.yyy.227: icmp_seq=1 ttl=225 time=0 ms

32 bytes from 194.x.yyy.227: icmp_seq=2 ttl=225 time=0 ms

32 bytes from 194.x.yyy.227: icmp_seq=3 ttl=225 time=0 ms

32 bytes from 194.x.yyy.227: icmp_seq=4 ttl=225 time=0 ms

32 bytes from 194.x.yyy.227: icmp_seq=5 ttl=225 time=0 ms

32 bytes from 194.x.yyy.227: icmp_seq=6 ttl=225 time=0 ms

……..

Countermeasures 

Echo Requests or Ping is widely carried out over the Internet and almost all servers are bound to encounter such instances (wherein they are on the receiving end) every now and then. One easy method in which you can block ping requests at the router level is by adding the following ACL: 

access-list 101 deny icmp any any 8 

This line will filter out all echo request packets at the router level and discard them. There will be no echo replies sent back to the attacker. However, if you want to filter out all echo requests coming to your network except those coming from your ISP, (who send out echo requests for routine checks) then the following ACL rules will do the job for you: 

access-list 101 permit icmp xx.xx.xx.xx 0.0.0.255 any 8

access-list 101 deny icmp any any 8 

The above access rules will discard all echo requests except those that are coming from your ISP, whose IP Address is xx.xx.xx.xx.  

Using the Timestamp Request and Timestamp Reply Messages 

The ICMP Timestamp Request message has a type value of 13, while an ICMP Timestamp Reply message has a type value of 14. These messages in conjugation with one another help one system to query another system for the current time in the latter system. The time returned is actually the number of milliseconds since midnight, Coordinated Universal Time. (UTC)  

The typical format of a Timestamp message is as follows:

 

0                         7    8                                     15      16                                                                  31

____________________________________________________________________

|                               |                                               |                                                                            | 

|   Type =13or14    |               Code =0                 |               16-Bit Checksum                               |

|                               |                                               |                                                                            |

|_____________ |____________________ |________________________________ |  

|                                                                               |                                                                            |

|               Identifier                                                |               sequence number                              |

|_________________________________  |_________________________________|

|                                                                                                                                                            |

|                                                                                                                                                            | 

|                                               32-bit originate timestamp                                                               | 

|                                                                                                                                                            |

|_________________________________________________________________  _|

|                                                                                                                                                            |                 

|                                                                                                                                                            | 

|                                               32-bit receive Timestamp                                                                 | 

|                                                                                                                                                            | 

|                                                                                                                                                            |

|__________________________________________________________________  |  

|                                                                                                                                                           |                  

|                                                                                                                                                           |  

|                                               32-bit transmit Timestamp                                                              |  

|                                                                                                                                                           |  

|                                                                                                                                                           |

|__________________________________________________________|

 

How exactly does the Timestamp process work? The sender prepares a datagram of the above type and fills in the time at which the message was sent in the Originate Timestamp field. Then, the receiving end fills in the time at which it receives the ICMP Time request message in the Receive Timestamp field. After that, the time at which the receiving end sends back the Timestamp reply message is recorded in the Transmit Timestamp field. However, nowadays the same value filled in the receive Timestamp field is filled in the transmit Timestamp field also. 

Thus, if you receive a Timestamp reply message from the remote system to which you had sent the Timestamp request message it means that particular host is alive.  

************************

HACKING TRUTH: One can also use the Timestamp messages to differentiate between the types of Operating System running on the target host. For Example, Some versions of Windows NT are known to not reply to a Timestamp request.

*********************** 

To actually implement the ICMP Timestamp feature, all you need is a Unix box and a kewl utility named icmpquery whose source code is as follows: (This utility can also be used for ICMP Address Mask Requests, which are discussed later in the manual)

----------------------

/*
 * icmpquery.c - send and receive ICMP queries for address mask
 *               and current time.
 *
 * Version 1.0
 *
 * Copyright 1998, 1998 David G. Andersen 
 *                                               http://www.angio.net/
 *
 * Verified to work on:
 *    FreeBSD (2.x, 3.x)
 *    Linux 2.0.x
 *    NetBSD 1.3
 *
 * Should work on Solaris and other platforms with BSD-ish stacks. *
 * If you compile it somewhere else, or it doesn't work somewhere,
 * please let me know. *
 * Compilation:  gcc icmpquery.c -o icmpquery
 */
 /* Some portions of this code are taken from FreeBSD's ping source.
 * Those portions are subject to the BSD copyright, which is appended
 * at the end of this file.
 */ 
#define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
#define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
#define SET(bit)        (A(bit) |= B(bit))
#define CLR(bit)        (A(bit) &= (~B(bit)))
#define TST(bit)        (A(bit) & B(bit)) 

#include <time.h>

#include <sys/time.h>

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netdb.h>

#include <netinet/in.h>

#include <netinet/in_systm.h>

#include <netinet/ip.h>

#include <netinet/ip_icmp.h>

#include <errno.h>

#include <string.h>

#include <signal.h> 

/*
 * We perform lookups on the hosts, and then store them in a chain
 * here.
 *
 * Yes, it's linear.  No, I don't care.
 */ 
struct hostdesc {
                    char *hostname;
                    struct in_addr hostaddr;
                    struct hostdesc *next;
}; 
struct hostdesc *hostnames;
struct hostdesc *hosttail;
 
void resolv_from(char *hostfrom, struct in_addr *fromaddr)
{
                    struct hostent *hp;
                    if (hostfrom == NULL) {
                                         fromaddr->s_addr = 0;
                                         return;
                    }
                    
                    if ((hp = gethostbyname(hostfrom)) == NULL) {
                                         if ((fromaddr->s_addr = inet_addr(hostfrom)) == -1) {
                                                             fprintf(stderr, "could not resolve from address\n");
                                                             exit(0);
                                         }
                    } else {
                                         bcopy(hp->h_addr_list[0], &fromaddr->s_addr, hp->h_length);
                    }
}
 
/*
 * Set up the list of hosts.  Return the count.
 */
 
int makehosts(char **hostlist)
{
                    int i;
                    struct hostent *hp;
                    struct in_addr tmpaddr;
                    int hostcount = 0;
                    
                    for (i = 0; hostlist[i]; i++) {
#ifdef DEBUG
                                         printf("Resolving %s\n", hostlist[i]);
#endif
                                         if ((hp = gethostbyname(hostlist[i])) == NULL) {
                                                             if (tmpaddr.s_addr = inet_addr(hostlist[i])) {
                                                                                 /* Could not resolve it.  Skip it. */
                                                                                 fprintf(stderr, "%s: unknown host\n",
                                                                                                      hostlist[i]);
                                                                                 continue;
                                                             }
                                         } else {
                                                             bcopy(hp->h_addr_list[0],
                                                                   &tmpaddr.s_addr, hp->h_length);
                                         }
 
                                         /* The host has been resolved.  Put it in the chain */
                                         /* We want to stick it on the end. */
                                         if (hostnames == NULL) {
                                                             hostnames = (struct hostdesc *)
                                                                                 malloc(sizeof(*hostnames));
                                                             if (hostnames == NULL) {
                                                                                 perror("hostnames malloc failed");
                                                                                 exit(-1);
                                                             }
                                                             hosttail = hostnames;
                                         } else {
                                                             hosttail->next = (struct hostdesc *)
                                                                                 malloc(sizeof(*hostnames));
                                                             if (hosttail->next == NULL) {
                                                                                 perror("hosttail->next malloc failed");
                                                                                 exit(-1);
                                                             }
                                                             hosttail = hosttail->next;
                                         }
                                         hosttail->hostname = strdup(hostlist[i]);
                                         if (hosttail->hostname == NULL) {
                                                             perror("strdup failed");
                                                             exit(-1);
                                         }
                                         hosttail->hostaddr = tmpaddr;
                                         hosttail->next = NULL;
                                         hostcount++;
                    }
                    return hostcount;
}
 
void usage(char *prog)
{
   fprintf(stderr,
                       "%s  <-querytype> [-f fromhost] [-d delay] [-T time] destination list\n"
                       "    where  is one of:\n"
                       "        -t : icmp timestamp request\n"
                       "        -m : icmp address mask request\n"
                       "    The delay is in microseconds to sleep between packets.\n"
                       "    The destination list is a list of hostnames or addresses\n"
                       "    -T specifies the number of seconds to wait for a host to\n"
                       "       respond.  The default is 5.\n"
                       "    If you're on a modem, you may wish to use a larger -d and -T\n"
                       , prog);
}
 
/*
 * Set up a packet.  Returns the length of the ICMP portion.
 */
 
int initpacket(char *buf, int querytype, struct in_addr fromaddr)
{
   struct ip *ip = (struct ip *)buf;
   struct icmp *icmp = (struct icmp *)(ip + 1);
   int on = 1;
 
   /* things we customize */
   int icmplen = 0;
   int icmptype = 0;
 
   int offset;
   int typevar;
   int subtype;
 
   ip->ip_src = fromaddr;                  /* if 0,  have kernel fill in */
   ip->ip_v = 4;                                   /* Always use ipv4 for now */
   ip->ip_hl = sizeof *ip >> 2;
   ip->ip_tos = 0;
   ip->ip_id = htons(4321);
   ip->ip_ttl = 255;
   ip->ip_p = 1;
   ip->ip_sum = 0;                 /* kernel fills in */
 
   icmp->icmp_seq = 1;
   icmp->icmp_cksum = 0;
   icmp->icmp_type = querytype;
   icmp->icmp_code = 0;
 
   switch(querytype) {
   case ICMP_TSTAMP:
                       gettimeofday( (struct timeval *)(icmp+8), NULL);
                       bzero( icmp+12, 8);
                       icmplen = 20;
                       break;
   case ICMP_MASKREQ:
                       *((char *)(icmp+8)) = 255;
                       icmplen = 12;
                       break;
   default:
                       fprintf(stderr, "eek: unknown query type\n");
                       exit(0);
   }
   ip->ip_len = sizeof(struct ip) + icmplen;
   return icmplen;
}
   
void sendpings(int s, int querytype, struct hostdesc *head, int delay,
                           struct in_addr fromaddr)
     
{
                    char buf[1500];
                    struct ip *ip = (struct ip *)buf;
                    struct icmp *icmp = (struct icmp *)(ip + 1);
                    struct sockaddr_in dst;
                    int icmplen;
 
                    bzero(buf, 1500);
                    icmplen = initpacket(buf, querytype, fromaddr);
                    dst.sin_family = AF_INET;
 
                    while (head != NULL) {
#ifdef DEBUG
                                         printf("pinging %s\n", head->hostname);
#endif
                                         ip->ip_dst.s_addr = head->hostaddr.s_addr;
                                         dst.sin_addr = head->hostaddr;
                                         icmp->icmp_cksum = 0;
                                         icmp->icmp_cksum = in_cksum(icmp, icmplen);
                                         if (sendto(s, buf, ip->ip_len, 0,
                                                                (struct sockaddr *)&dst,
                                                                sizeof(dst)) < 0) {
                                                             perror("sendto");
                                         }
                                         if (delay)
                                                             usleep(delay);
                                         /* Don't flood the pipeline..kind of arbitrary */
                                         head = head->next;
                    }
}
 
void myexit(int whatsig)
{
                    exit(0);
}
 
/*
 * Listen for 'hostcount' pings, print out the information, and
 * then exit.
 */
 
void recvpings(int s, int querytype, struct hostdesc *head, int hostcount)
{
                    char buf[1500];
                    struct ip *ip = (struct ip *)buf;
                    struct icmp *icmp;
                    int err = 0;
                    long int fromlen = 0;
                    int hlen;
                    struct timeval *tp;
                    struct timeval tv;
                    int recvd = 0;
                    char *hostto;
                    char hostbuf[128];  /* We'll print the address here otherwise */
                    struct hostdesc *foundhost;
 
                    gettimeofday(&tv, NULL);
 
                    while (recvd < hostcount) {
                                         if ((err = recvfrom(s, buf, sizeof buf, 0, NULL,
                                                                                     (int *)&fromlen)) < 0)
                                         {
                                                             perror("icmpquery:  recvfrom");
                                         }
      
                                         hlen = ip->ip_hl << 2;
                                         icmp = (struct icmp *)(buf + hlen);
 
                                         /* Find the host */
                                         hostto = 0;
                                         for (foundhost = head; foundhost != NULL;
                                              foundhost = foundhost->next) {
                                                             if (foundhost->hostaddr.s_addr == ip->ip_src.s_addr) {
                                                                                 hostto = foundhost->hostname;
                                                                                 break;
                                                             }
                                         }
 
                                         if (!hostto) {
                                                             sprintf(hostbuf, "unknown (%s)",
                                                                                 inet_ntoa(ip->ip_src));
                                                             hostto = hostbuf;
                                         }
                                         
                                         /* For time */
                                         switch(icmp->icmp_type) {
                                         case ICMP_TSTAMPREPLY:
                                                             fromlen = ntohl(icmp->icmp_ttime);
                                                              /* ms since midnight. yuch. */
                                                             tv.tv_usec = fromlen;
                                                             tv.tv_usec = 0;
                                                             tv.tv_sec -= tv.tv_sec%(24*60*60);
                                                             tv.tv_sec += (fromlen/1000);
                                                             tv.tv_usec = (fromlen%1000);
                                                             printf("%-40.40s:  %s", hostto, ctime(&(tv.tv_sec)));
                                                             break;
 
                                         case ICMP_MASKREPLY:
                                                             fromlen = ntohl(icmp->icmp_dun.id_mask);
                                                             printf("%-40.40s:  0x%lX\n", hostto, fromlen);
                                                             break;
 
                                         default:
                                                             printf("Unknown ICMP message received (type %d)\n",
                                                                    icmp->icmp_type);
                                         }
                                         recvd++;
                    }
}
 
int
main(int argc, char **argv)
{
   int s;
 
   char *progname;
   extern char *optarg;         /* getopt variable declarations */
   char *hostfrom = NULL;
   extern int optind;
   extern int optopt;
   extern int opterr;
   char ch;                     /* Holds the getopt result */
   int on = 1;
   int hostcount;
   int delay = 0;
   int querytype = ICMP_TSTAMP;
   struct in_addr fromaddr;
   int timeout = 5;  /* Default to 5 seconds */
 
   fromaddr.s_addr = 0;
 
   progname = argv[0];
 
   while ((ch = getopt(argc, argv, "tmf:d:T:")) != EOF) 
      switch(ch)
      {
      case 'd':
                          delay = (int) strtol(optarg, NULL, 10);
                          break;
      case 't': /* timestamp request */
                          querytype = ICMP_TSTAMP;
                          break;
      case 'm': /* address mask request */
                          querytype = ICMP_MASKREQ;
                          break;
      case 'f':
                          hostfrom = optarg;
                          resolv_from(hostfrom, &fromaddr);
                          break;
      case 'T':
                          timeout = (int) strtol(optarg, NULL, 10);
                          break;
      default:
                          usage(progname);
                          exit(-1);
      }
   argc -= optind;
   argv += optind;
 
   if (!argv[0] || !strlen(argv[0])) 
   {
      usage(progname);
      exit(-1);
   }
 
   hostcount = makehosts(argv);
 
   if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
      perror("socket");
      exit(1);
   }
   if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
      perror("IP_HDRINCL");
      exit(1);
   }
 
   signal(SIGALRM, myexit);
   alarm(timeout);
   sendpings(s, querytype, hostnames, delay, fromaddr);
   recvpings(s, querytype, hostnames, hostcount);
}
   
/*
 * in_cksum --
 *                 Checksum routine for Internet Protocol family headers (C Version)
 *      From FreeBSD's ping.c
 */
 
in_cksum(addr, len)
                    u_short *addr;
                    int len;
{
                    register int nleft = len;
                    register u_short *w = addr;
                    register int sum = 0;
                    u_short answer = 0;
 
                    /*
                     * Our algorithm is simple, using a 32 bit accumulator (sum), we add
                     * sequential 16 bit words to it, and at the end, fold back all the
                     * carry bits from the top 16 bits into the lower 16 bits.
                     */
                    while (nleft > 1)  {
                                         sum += *w++;
                                         nleft -= 2;
                    }
 
                    /* mop up an odd byte, if necessary */
                    if (nleft == 1) {
                                         *(u_char *)(&answer) = *(u_char *)w ;
                                         sum += answer;
                    }
 
                    /* add back carry outs from top 16 bits to low 16 bits */
                    sum = (sum >> 16) + (sum & 0xffff);                   /* add hi 16 to low 16 */
                    sum += (sum >> 16);                                             /* add carry */
                    answer = ~sum;                                                                          /* truncate to 16 bits */
                    return(answer);
}
 
 
/*
 * Copyright (c) 1989, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Mike Muuss.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

-----------------------

 

The following was carried out on a Linux system, using a modified version of the icmpquery utility:

 

#ankitbox icmpquery –t xx.xx.xx.xx

xx.xx.xx.xx                                                             : 04:15:19

 

An easier manner, in which one can obtain the system time of a remote system, is by telnetting to its daytime port.

 

Countermeasures

 

One easy method in which you can block Timestamp messages at the router level is by adding the following ACL’s:

 

access-list 101 permit icmp xx.xx.xx.xx 0.0.0.255 any 13

access-list 101 deny icmp any any 13

 

The above access rules will discard all Timestamp requests except those that are coming from your ISP, who’s IP Address is xx.xx.xx.xx.

 

Using the Address Mask Request and Address Mask Reply Messages

 

The Address Mask Request message has a type value of 17, while an Address Mask Reply Message has a type value of 18. The address mask request and reply messages are used in order to obtain the address mask of the target system.

 

Once we know the IP Address of the remote system, then all we need to do is send an Address Mask request message to the broadcast address of the network in which that system resides. Once we do that, we will receive an ICMP Address Mask Reply message containing the Subnet Address of the target system.

The typical structure of Address Mask Request and reply message is as follows:

 

 

0                         7    8                                     15      16                                                             31

____________________________________________________________________

|                               |                                               |                                                                             |

|     Type =17, 18    |               Code =0                 |               16-Bit Checksum                                |

|                               |                                               |                                                                             |

|_____________ |___________________   |_________________________________| 

|                                                                               |                                                                             |

|               Identifier                                                |               sequence number                               |

|__________________________________|_________________________________|

|                                                                                                                                                             |

|                                                                                                                                                             |

|                                                           32-bit Subnet Mask                                                               |

|                                                                                                                                                             |

|___________________________________________________________________ |

 

 

The sender system fills in the Identifier and Sequence Number fields, while the receiving end replies with these fields and its Subnet Mask in the Subnet Mask field. As a result, the bottom line being, the ICMP Address Mask messages can be used to obtain the Subnet Mask of the target system.

 

Again, in order to implement ICMP Address Mask Requests, one can use the nifty utility named icmpquery:

 

#ankitbox icmpquery –m xx.xx.xx.xx

xx.xx.xx.xx                                                             : The Subnet Mask Here

 

Countermeasures

 

One easy method in which you can block ICMP Address Mask request messages at the router level is by adding the following ACL’s:

 

access-list 101 permit icmp xx.xx.xx.xx 0.0.0.255 any 17

access-list 101 deny icmp any any 17

 

The above access rules will discard all Address Mask requests except those that are coming from your ISP, who’s IP Address is xx.xx.xx.xx.

 

Remote OS Detection with ICMP Messages

 

There is a choice of a variety of different operating systems that a system administrator can choose from to run on his systems. After port scanning and getting a list of all open ports and daemons running on the target system, the next step that a hacker typically would want to take is to detect the exact Operating System name and version running on the target system.  

Although one can easily get the exact Operating System name and version running on the remote system by daemon banner grabbing, HTTP error messages, FTP commands etc  even ICMP can be used to get the same information. 

Each operating system responds differently to a particular kind of a datagram or ICMP message. This means that a response that we get by sending a particular ICMP message to a Windows system would be different from the response we get by sending the exact same ICMP message to a Unix system. Different operating systems respond differently to the same ICMP message. In other words, the type of response that we get on sending an ICMP packet to a particular remote system depends on the type of Operating System running on it. As a result, by studying the response that we get on sending ICMP messages to a remote system, we can easily differentiate between the various Operating Systems possibly running on it and deduce the exact OS name and version, actually running on it.  

The key to deducing the exact OS name and version running on a remote system is to know the differences existing in the responses made by the various operating systems to the particular ICMP message that you are using as a differentiating benchmark. Thus to deduce the OS name and version of a remote host by ICMP messages, simply follow the below process: 

1.        Send particular ICMP messages to the remote host.

2.        Record the response that you get by the remote system, when you perform Step 1.

3.        Compare the response received, to the already known responses shown by the various Operating Systems so that you can deduce the exact OS name and version running on the remote host.  

NOTE: These differences in the response by various Operating Systems are also found to be due to improper implementation and configuration. For a detailed look into these differences read the following manual: http://www.sys-security.com/archive/papers/ICMP_Scanning_v3.0.zip 

There are a number of different ICMP OS Detection techniques: (This document will provide only any introduction to the various processes) 

ICMP Message Quoting 

Each time an error is encountered in the data transfer process, the remote host calls upon ICMP and generates an ICMP error message. Different operating systems quote different amounts of information in the error message generated, each time such errors are encountered. On analysis of these errors messages sent by the remote host, we can deduce (or rather assume) the remote Operating System. 

ICMP Error Message Quenching 

RFC 1812 lays down rules according to which Operating Systems limit the rate at which error messages are sent. One can easily send UDP packets to a random unused port, so as to make the remote host generate and send back an ICMP Unreachable error message. If we then count the number of such Unreachable Error messages sent back from the remote host to our system in a given amount of time, then we can easily deduce the operating system running there.  

******************

HACKING TRUTH: By Examining the Type of Service (TOS) in ICMP port Unreachable error messages too, we can determine the Operating System running on the target system.

****************** 

ICMP Error Message: Echoing Integrity 

Certain systems are known to alter the IP Headers of the ICMP error messages sent back by them. If we analyze the extent and type of alterations made by the remote system in the IP header, we can deduce to a certain extend the operating system running on the target system. 

Remote Firewall Detection with ICMP 

There may be occasions when you suspect that a firewall has been installed in the target system’s network, which is filtering out all your attacks. Does ICMP have a solution for detection of firewalls too? Yes, it indeed has a solution in the form of the popular Unix utility traceroute.  

Traceroute makes use of ICMP i.e. ‘The Time Exceeded in Transit error message’, ‘Port Unreachable Error Message’ and the Time to Live (TTL) field in the IP Header to trace out the route from your system to the target system.  

The typical format of a Time Exceeded In transit Error Message, which has a type value of 11 and a code value of 0 is as follows: 

0                         7    8                                     15      16                                                                   31

____________________________________________________________________

|                               |                                               |                                                                             |

|     Type =11          |               Code =0                 |               16-Bit Checksum                                |

|                               |                                               |                                                                             |

|_____________ |____________________ |_________________________________ |               

|                                                                               |                                                                             |

|               Identifier                                                |               sequence number                               |

|__________________________________|_________________________________|

|                                                                                                                                                             |

|                                                                                                                                                             |

|                               IP Header + first 8 bytes of Original IP Datagram data                                 |

|                                                                                                                                                              |

|____________________________________________________________________|

 

 

To detect a firewall installed or a router (which discards ICMP TTL expired packets) in your path to the remote target system, you can use the traceroute utility:

 

host2 # traceroute xyz.com

traceroute to xyz.com  (202.xx.12.34), 30 hops max, 40 byte packets

     1         isp.net    (202.xy.34.12)        20ms       10ms       10ms

     2         xyz.com (202.xx.12.34)          130ms     130ms     130ms

     3        *               *              * 

The asterix signify that a firewall or a filtering router has been encountered. Please note that the asterix does not necessarily mean that the filtering device has been installed in your target system network, but simply means that a filtering device has been encountered in the path to the target system. It could also simply signify a filtering device installed by your own ISP, which discards all ICMP TTL expired packets. 

Since this manual is about the ICMP protocol, we would not be discussing, but would simply be mentioning the other methods of detecting a filtering device: 

1.        Banner Grabbing

2.        Using nmap

3.        Port Scanning and looking for specific ports on which particular firewalls are known to run by default. 

Countermeasures 

You can stop attackers from detecting a filtering device installed in your network, by adding the following ACL to the router: 

access-list 101 deny ip any any 11 

It configures the routers to not respond to TTL expired messages, when it receives a packet with TTL equal to 0.    
Footnote