Nmap Development mailing list archives

[PATCH] IP Protocol Ping


From: Kris Katterjohn <katterjohn () gmail com>
Date: Wed, 24 Oct 2007 17:53:18 -0500

Hey everyone!

I have attached a patch to implement a new ping type, much like the
current IPProto scan (-sO).  Hopefully it'll slip by some setups to
reveal up hosts, or provide a less-watched host discovery mechanism.

It's used with "-PO" (the letter O) to go along with "-sO", and I
renamed references to "-P0" (zero) to "-PN".  Fyodor agreed with this
change, so I'm not completely messing everything up :)

I haven't changed the refguide for this patch (to add -PO and change
-P0) because that would've been too big I think.

Currently the default is to send with protocols numbers 1 (ICMP), 2
(IGMP), 6 (TCP), and 17 (UDP).  1, 6, and 17 generally reply with the
protocol rather than ICMP Protocol Unreachables.  2 now seems to
generally return Proto Unreachables despite me adding a header to it a
long time ago (last year sometime I believe).

Some stats:

500 random hosts (the same for both):

-PS21,22,23,25,80,113: 27 up
-PO: 30 up

5000 random hosts, solely using IGMP:

-PO2: 37 up (29 of which were Protocol Unreachables)

I've had hosts get picked up with Protocol Unreachables that weren't
picked up with other ping types, which is one of the things I hoped for
when I thought of this.

This can also be seen as a "general" ping type as by default it sends
the main protocols (using this I've gotten hosts that send back all the
different protocol responses that wouldn't have been picked up with
specifying all the different kinds).

Please test and, as usual, any comments or suggestions are welcome!

Thanks,
Kris Katterjohn
Index: targets.cc
===================================================================
--- targets.cc  (revision 6046)
+++ targets.cc  (working copy)
@@ -503,7 +503,7 @@
         3) We are doing a raw-mode portscan or osscan OR
         4) We are on windows and doing ICMP ping */
       if (o.isr00t && o.af() == AF_INET && 
-         ((pingtype & (PINGTYPE_TCP|PINGTYPE_UDP|PINGTYPE_ARP)) || o.RawScan()
+         ((pingtype & (PINGTYPE_TCP|PINGTYPE_UDP|PINGTYPE_PROTO|PINGTYPE_ARP)) || o.RawScan()
 #ifdef WIN32
           || (pingtype & (PINGTYPE_ICMP_PING|PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS))
 #endif // WIN32
Index: nmap.cc
===================================================================
--- nmap.cc     (revision 6046)
+++ nmap.cc     (working copy)
@@ -224,9 +224,10 @@
        "HOST DISCOVERY:\n"
        "  -sL: List Scan - simply list targets to scan\n"
        "  -sP: Ping Scan - go no further than determining if host is online\n"
-       "  -P0: Treat all hosts as online -- skip host discovery\n"
+       "  -PN: Treat all hosts as online -- skip host discovery\n"
        "  -PS/PA/PU [portlist]: TCP SYN/ACK or UDP discovery to given ports\n"
        "  -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes\n"
+       "  -PO [protocol list]: IP Protocol Ping\n"
        "  -n/-R: Never do DNS resolution/Always resolve [default: sometimes]\n"
        "  --dns-servers <serv1[,serv2],...>: Specify custom DNS servers\n"
        "  --system-dns: Use OS's DNS resolver\n"
@@ -317,7 +318,7 @@
        "EXAMPLES:\n"
        "  nmap -v -A scanme.nmap.org\n"
        "  nmap -v -sP 192.168.0.0/16 10.0.0.0/8\n"
-       "  nmap -v -iR 10000 -P0 -p 80\n"
+       "  nmap -v -iR 10000 -PN -p 80\n"
        "SEE THE MAN PAGE FOR MANY MORE OPTIONS, DESCRIPTIONS, AND EXAMPLES\n", NMAP_NAME, NMAP_VERSION, NMAP_URL);
   exit(rc);
 }
@@ -1081,9 +1082,20 @@
          o.ping_ackprobes[0] = DEFAULT_TCP_PROBE_PORT;
        }
       } else if (*optarg == 'O') {
-       fatal("-PO (the letter O)? No such option. Perhaps you meant to disable pings with -P0 (Zero).");
+       o.pingtype |= PINGTYPE_PROTO;
+       if (isdigit((int) *(optarg+1))) {
+         o.num_ping_protoprobes = numberlist2array(optarg+1, o.ping_protoprobes, sizeof(o.ping_protoprobes), &proberr);
+         if (o.num_ping_protoprobes < 0) {
+           fatal("Bogus argument to -PO: %s", proberr);
+         }
+       }
+       if (o.num_ping_protoprobes == 0) {
+         u16 probes[] = DEFAULT_PROTO_PROBE_PORTS;
+         o.num_ping_protoprobes = sizeof probes / sizeof *probes;
+         memcpy(o.ping_protoprobes, probes, sizeof probes);
+       }
       } else { 
-       fatal("Illegal Argument to -P, use -P0, -PI, -PB, -PE, -PM, -PP, -PA, -PU, -PT, or -PT80 (or whatever number 
you want for the TCP probe destination port)"); 
+       fatal("Illegal Argument to -P, use -PN, -PO, -PI, -PB, -PE, -PM, -PP, -PA, -PU, -PT, or -PT80 (or whatever 
number you want for the TCP probe destination port)"); 
       }
       break;
     case 'p': 
Index: nmap.h
===================================================================
--- nmap.h      (revision 6046)
+++ nmap.h      (working copy)
@@ -287,6 +287,8 @@
                                      to 113 */
 #define DEFAULT_UDP_PROBE_PORT 31338 /* The port UDP probes (esp. "ping" probes) go to if unspecified
                                     by user */
+#define DEFAULT_PROTO_PROBE_PORTS { 1, 2, 6, 17 } /* The IPProto ping probes to use if unspecified
+                                                    by user */
 
 #define MAX_DECOYS 128 /* How many decoys are allowed? */
 
@@ -351,6 +353,7 @@
 #define PINGTYPE_CONNECTTCP 256
 #define PINGTYPE_UDP  512
 #define PINGTYPE_ARP 1024
+#define PINGTYPE_PROTO 2048
 
 #define DEFAULT_PING_TYPES PINGTYPE_TCP|PINGTYPE_TCP_USE_ACK|PINGTYPE_ICMP_PING
 
Index: NmapOps.cc
===================================================================
--- NmapOps.cc  (revision 6046)
+++ NmapOps.cc  (working copy)
@@ -202,7 +202,7 @@
   reference_FPs = NULL;
   magic_port = 33000 + (get_random_uint() % 31000);
   magic_port_set = 0;
-  num_ping_synprobes = num_ping_ackprobes = num_ping_udpprobes = 0;
+  num_ping_synprobes = num_ping_ackprobes = num_ping_udpprobes = num_ping_protoprobes = 0;
   timing_level = 3;
   max_parallelism = 0;
   min_parallelism = 0;
@@ -332,11 +332,11 @@
   }
 
   if (pingtype != PINGTYPE_NONE && spoofsource) {
-    error("WARNING:  If -S is being used to fake your source address, you may also have to use -e <interface> and -P0 
.  If you are using it to specify your real source address, you can ignore this warning.");
+    error("WARNING:  If -S is being used to fake your source address, you may also have to use -e <interface> and -PN 
.  If you are using it to specify your real source address, you can ignore this warning.");
   }
 
   if (pingtype != PINGTYPE_NONE && idlescan) {
-    error("WARNING: Many people use -P0 w/Idlescan to prevent pings from their true IP.  On the other hand, timing 
info Nmap gains from pings can allow for faster, more reliable scans.");
+    error("WARNING: Many people use -PN w/Idlescan to prevent pings from their true IP.  On the other hand, timing 
info Nmap gains from pings can allow for faster, more reliable scans.");
     sleep(2); /* Give ppl a chance for ^C :) */
   }
 
@@ -352,13 +352,16 @@
    fatal("Sorry, UDP Ping (-PU) only works if you are root (because we need to read raw responses off the wire) and 
only for IPv4 (cause fyodor is too lazy right now to add IPv6 support and nobody has sent a patch)");
  }
 
+ if ((pingtype & PINGTYPE_PROTO) && (!isr00t || af() != AF_INET)) {
+   fatal("Sorry, IPProto Ping (-PO) only works if you are root (because we need to read raw responses off the wire) 
and only for IPv4");
+ }
 
  if (ipprotscan + (TCPScan() || UDPScan()) + listscan + pingscan > 1) {
    fatal("Sorry, the IPProtoscan, Listscan, and Pingscan (-sO, -sL, -sP) must currently be used alone rather than 
combined with other scan types.");
  }
 
  if ((pingscan && pingtype == PINGTYPE_NONE)) {
-    fatal("-P0 (skip ping) is incompatable with -sP (ping scan).  If you only want to enumerate hosts, try list scan 
(-sL)");
+    fatal("-PN (skip ping) is incompatable with -sP (ping scan).  If you only want to enumerate hosts, try list scan 
(-sL)");
   }
 
  if (pingscan && (TCPScan() || UDPScan() || ipprotscan || listscan)) {
@@ -413,7 +416,7 @@
   }
   
   if (bouncescan && pingtype != PINGTYPE_NONE) 
-    log_write(LOG_STDOUT, "Hint: if your bounce scan target hosts aren't reachable from here, remember to use -P0 so 
we don't try and ping them prior to the scan\n");
+    log_write(LOG_STDOUT, "Hint: if your bounce scan target hosts aren't reachable from here, remember to use -PN so 
we don't try and ping them prior to the scan\n");
   
   if (ackscan+bouncescan+connectscan+finscan+idlescan+maimonscan+nullscan+synscan+windowscan+xmasscan > 1)
     fatal("You specified more than one type of TCP scan.  Please choose only one of -sA, -b, -sT, -sF, -sI, -sM, -sN, 
-sS, -sW, and -sX");
Index: tcpip.cc
===================================================================
--- tcpip.cc    (revision 6046)
+++ tcpip.cc    (working copy)
@@ -949,7 +949,7 @@
           "LINUX: If you are getting Socket type not supported, try modprobe af_packet or recompile your kernel with 
SOCK_PACKET enabled.\n"
           "*BSD:  If you are getting device not configured, you need to recompile your kernel with Berkeley Packet 
Filter support.  If you are getting No such file or directory, try creating the device (eg cd /dev; MAKEDEV <device>; 
or use mknod).\n"
           "*WINDOWS:  Nmap only supports ethernet interfaces on Windows for most operations because Microsoft disabled 
raw sockets as of Windows XP SP2.  Depending on the reason for this error, it is possible that the --unprivileged 
command-line argument will help.\n"
-          "SOLARIS:  If you are trying to scan localhost and getting '/dev/lo0: No such file or directory', complain 
to Sun.  I don't think Solaris can support advanced localhost scans.  You can probably use \"-P0 -sT localhost\" 
though.\n\n", pcapdev, snaplen, promisc, to_ms, err0r);
+          "SOLARIS:  If you are trying to scan localhost and getting '/dev/lo0: No such file or directory', complain 
to Sun.  I don't think Solaris can support advanced localhost scans.  You can probably use \"-PN -sT localhost\" 
though.\n\n", pcapdev, snaplen, promisc, to_ms, err0r);
       } else {
        error("pcap_open_live(%s, %d, %d, %d) FAILED. Reported error: %s.  Will wait %d seconds then retry.", pcapdev, 
snaplen, promisc, to_ms, err0r, (int) pow(5.0, failed)); 
       }
Index: targets.h
===================================================================
--- targets.h   (revision 6046)
+++ targets.h   (working copy)
@@ -158,7 +158,8 @@
   unsigned int rawicmpscan: 1,
     connecttcpscan: 1,
     rawtcpscan: 1,
-    rawudpscan: 1;
+    rawudpscan: 1,
+    rawprotoscan: 1;
 };
 
 
Index: output.cc
===================================================================
--- output.cc   (revision 6046)
+++ output.cc   (working copy)
@@ -1796,7 +1796,7 @@
     error("WARNING: No targets were specified, so 0 hosts scanned.");
   if (o.numhosts_scanned == 1 && o.numhosts_up == 0 && !o.listscan && 
       o.pingtype != PINGTYPE_NONE)
-    log_write(LOG_STDOUT, "Note: Host seems down. If it is really up, but blocking our ping probes, try -P0\n");
+    log_write(LOG_STDOUT, "Note: Host seems down. If it is really up, but blocking our ping probes, try -PN\n");
   else if (o.numhosts_up > 0) {
     if (o.osscan && o.servicescan)
       log_write(LOG_PLAIN, "OS and Service detection performed. Please report any incorrect results at 
http://insecure.org/nmap/submit/ .\n");
Index: scan_engine.cc
===================================================================
--- scan_engine.cc      (revision 6046)
+++ scan_engine.cc      (working copy)
@@ -424,6 +424,9 @@
   /* The index of the next UDP port in o.ping_udpprobes to probe during ping
      scan. */
   int next_udpportpingidx;
+  /* The index of the next IP protocol in o.ping_protoprobes to probe during ping
+     scan. */
+  int next_protoportpingidx;
   /* Whether we have sent an ICMP echo request. */
   bool sent_icmp_ping;
   /* Whether we have sent an ICMP address mask request. */
@@ -968,6 +971,7 @@
   next_ackportpingidx = 0;
   next_synportpingidx = 0;
   next_udpportpingidx = 0;
+  next_protoportpingidx = 0;
   sent_icmp_ping = false;
   sent_icmp_mask = false;
   sent_icmp_ts = false;
@@ -1220,7 +1224,7 @@
 bool UltraScanInfo::isRawScan() {
   return scantype != CONNECT_SCAN
     && (tcp_scan || udp_scan || prot_scan || ping_scan_arp
-      || (ping_scan && (ptech.rawicmpscan || ptech.rawtcpscan || ptech.rawudpscan)));
+      || (ping_scan && (ptech.rawicmpscan || ptech.rawtcpscan || ptech.rawudpscan || ptech.rawprotoscan)));
 }
 
  /* A circular buffer of the incompleteHosts.  nextIncompleteHost() gives
@@ -1325,6 +1329,8 @@
       else
         ptech.connecttcpscan = 1;
     }
+    if (o.pingtype & PINGTYPE_PROTO)
+      ptech.rawprotoscan = 1;
     if (o.pingtype & PINGTYPE_CONNECTTCP)
       ptech.connecttcpscan = 1;
     break;
@@ -1417,6 +1423,8 @@
       if (o.pingtype & PINGTYPE_ICMP_TS)
         numprobes++;
     }
+    if (ptech.rawprotoscan)
+      numprobes += o.num_ping_protoprobes;
     if (ptech.connecttcpscan)
       numprobes += o.num_ping_synprobes;
   } else assert(0); /* TODO: RPC scan */
@@ -1722,6 +1730,11 @@
         return 0;
       }
     }
+    if (USI->ptech.rawprotoscan) {
+      pspec->type = PS_PROTO;
+      pspec->proto = o.ping_protoprobes[hss->next_protoportpingidx++];
+      return 0;
+    }
     if (USI->ptech.connecttcpscan && hss->next_synportpingidx < o.num_ping_synprobes) {
       pspec->type = PS_CONNECTTCP;
       pspec->proto = IPPROTO_TCP;
@@ -1771,6 +1784,8 @@
       if ((o.pingtype & PINGTYPE_ICMP_TS) && !sent_icmp_ts)
         num_probes++;
     }
+    if (USI->ptech.rawprotoscan)
+      num_probes += o.num_ping_protoprobes - next_protoportpingidx;
     if (USI->ptech.connecttcpscan && next_synportpingidx < o.num_ping_synprobes)
       num_probes += o.num_ping_synprobes - next_synportpingidx;
     return num_probes;
@@ -2910,7 +2925,7 @@
                            UltraProbe *probe) {
   UltraProbe *newProbe = NULL;
   if (probe->type == UltraProbe::UP_IP) {
-    if (USI->prot_scan)
+    if (USI->prot_scan || USI->ptech.rawprotoscan)
       newProbe = sendIPScanProbe(USI, hss, probe->pspec(), 
                                 probe->tryno + 1, 0);
     else if (probe->protocol() == IPPROTO_TCP) {
@@ -3856,6 +3871,34 @@
     if (ip->ip_hl < 5)
       continue;
 
+    if (USI->ptech.rawprotoscan) {
+      memset(&sin, 0, sizeof(sin));
+      sin.sin_addr.s_addr = ip->ip_src.s_addr;
+      sin.sin_family = AF_INET;
+      hss = USI->findHost((struct sockaddr_storage *) &sin);
+      if (hss) {
+       setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0);
+       probeI = hss->probes_outstanding.end();
+       listsz = hss->num_probes_outstanding();
+       goodone = false;
+       for(probenum = 0; probenum < listsz && !goodone; probenum++) {
+         probeI--;
+         probe = *probeI;
+           
+         if (probe->protocol() == ip->ip_p) {
+           /* if this is our probe we sent to localhost, then it doesn't count! */
+           if (ip->ip_src.s_addr == ip->ip_dst.s_addr &&
+               probe->ipid() == ntohs(ip->ip_id))
+             continue;
+
+           newstate = HOST_UP;
+           current_reason = ER_PROTORESPONSE;
+           goodone = true;
+          }
+        }
+      }
+    }
+
     /* First check if it is ICMP, TCP, or UDP */
     if (ip->ip_p == IPPROTO_ICMP) {
       /* if it is our response */
@@ -3931,13 +3974,15 @@
         }
 
         struct ip *ip2 = (struct ip *) ((char *) ip + ip->ip_hl * 4 + 8);
-        if (bytes < ip->ip_hl * 4 + 8U + ip2->ip_hl * 4 + 8U) {
+
+       /* IPProto Scan (generally) sends bare IP headers, so no extra payload */
+        if (bytes < ip->ip_hl * 4 + 8U + ip2->ip_hl * 4 + 8U && !USI->ptech.rawprotoscan) {
           if (o.debugging)
             error("ICMP (embedded) type %d code %d packet is only %d bytes\n", ping->type, ping->code, bytes);
           continue;
         }
 
-        if (ip2->ip_p == IPPROTO_ICMP) {
+        if (ip2->ip_p == IPPROTO_ICMP && !USI->ptech.rawprotoscan) {
           /* The response was based on a ping packet we sent */
           if (!USI->ptech.rawicmpscan) {
             if (o.debugging)
@@ -3976,7 +4021,7 @@
           /* Did we fail to find a probe? */
           if (probenum >= listsz)
             continue;
-        } else if (ip2->ip_p == IPPROTO_TCP) {
+        } else if (ip2->ip_p == IPPROTO_TCP && !USI->ptech.rawprotoscan) {
           /* The response was based our TCP probe */
           if (!USI->ptech.rawtcpscan) {
             if (o.debugging)
@@ -4031,7 +4076,7 @@
           /* Did we fail to find a probe? */
           if (probenum >= listsz)
             continue;
-        } else if (ip2->ip_p == IPPROTO_UDP) {
+        } else if (ip2->ip_p == IPPROTO_UDP && !USI->ptech.rawprotoscan) {
           /* The response was based our UDP probe */
           if (!USI->ptech.rawudpscan) {
             if (o.debugging)
@@ -4083,7 +4128,40 @@
           /* Did we fail to find a probe? */
           if (probenum >= listsz)
             continue;
-        } else {
+        } else if (USI->ptech.rawprotoscan) {
+          memset(&sin, 0, sizeof(sin));
+          sin.sin_addr.s_addr = ip2->ip_dst.s_addr;
+          sin.sin_family = AF_INET;
+          hss = USI->findHost((struct sockaddr_storage *) &sin);
+          if (!hss) continue; // Not referring to a host that interests us
+          setTargetMACIfAvailable(hss->target, &linkhdr, ip, 0);
+          probeI = hss->probes_outstanding.end();
+          listsz = hss->num_probes_outstanding();
+
+          /* Find the probe that provoked this response. */
+          for (probenum = 0; probenum < listsz; probenum++) {
+            probeI--;
+            probe = *probeI;
+            goodseq = false;
+
+            if (o.af() != AF_INET || probe->protocol() != ip2->ip_p)
+              continue;
+
+            if (!allow_ipid_match(probe->ipid(), ntohs(ip2->ip_id)))
+              continue;
+
+            /* Ensure the connection info matches. */
+            if (hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr)
+              continue;
+
+            /* If we made it this far, we found it. We don't yet know if it's
+               going to change a host state (goodone) or not. */
+            break;
+         }
+          /* Did we fail to find a probe? */
+          if (probenum >= listsz)
+            continue;
+       } else {
           if (o.debugging)
             error("Got ICMP response to a packet which was not TCP, UDP, or ICMP");
           continue;
@@ -4119,7 +4197,7 @@
           log_write(LOG_STDOUT, "Got ICMP message type %d code %d\n", ping->type, ping->code);
         }
       }
-    } else if (ip->ip_p == IPPROTO_TCP) {
+    } else if (ip->ip_p == IPPROTO_TCP && !USI->ptech.rawprotoscan) {
       if (!USI->ptech.rawtcpscan) {
         continue;
       }
@@ -4202,7 +4280,7 @@
         if (o.debugging)
           log_write(LOG_STDOUT, "We got a TCP ping packet back from %s port %hi (trynum = %d)\n", 
inet_ntoa(ip->ip_src), ntohs(tcp->th_sport), trynum);
       }
-    } else if (ip->ip_p == IPPROTO_UDP) {
+    } else if (ip->ip_p == IPPROTO_UDP && !USI->ptech.rawprotoscan) {
       if (!USI->ptech.rawudpscan) {
         continue;
       }
@@ -4253,7 +4331,7 @@
         if (o.debugging)
           log_write(LOG_STDOUT, "In response to UDP-ping, we got UDP packet back from %s port %hi (trynum = %d)\n", 
inet_ntoa(ip->ip_src), htons(udp->uh_sport), trynum);
       }
-    } else if (o.debugging) {
+    } else if (!USI->ptech.rawprotoscan && o.debugging) {
       error("Found whacked packet protocol %d in %s.", ip->ip_p, __func__);
     }
   } while (!goodone && !timedout);
@@ -4349,7 +4427,7 @@
     if (len < 0 || len >= (int) sizeof(pcap_filter))
       fatal("ran out of space in pcap filter");
     filterlen = len;
-  } else if (USI->prot_scan) {
+  } else if (USI->prot_scan || (USI->ping_scan && USI->ptech.rawprotoscan)) {
     if (doIndividual)
       len = Snprintf(pcap_filter, sizeof(pcap_filter), 
                     "dst host %s and (icmp or (%s))", 
Index: NmapOps.h
===================================================================
--- NmapOps.h   (revision 6046)
+++ NmapOps.h   (working copy)
@@ -196,6 +196,8 @@
   u16 ping_ackprobes[MAX_PROBE_PORTS];
   int num_ping_udpprobes;
   u16 ping_udpprobes[MAX_PROBE_PORTS];
+  int num_ping_protoprobes;
+  u16 ping_protoprobes[MAX_PROBE_PORTS];
   /* Scan timing/politeness issues */
   int timing_level; // 0-5, corresponding to Paranoid, Sneaky, Polite, Normal, Aggressive, Insane
   int max_parallelism; // 0 means it has not been set

Attachment: signature.asc
Description: OpenPGP digital signature


_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: