Snort, NMAP Ping scan and (fast) one line hacks

Last week I was in Barcelona helping some colleagues when a client called asking for a list of “running” clients in his network. We had a VPN connection to this net and the customer itself said that “it didn’t need an accurate list, just to have an idea” so we agreed that a simple ICMP scan would be enough.

This looked like the perfect ocassion to show off my Nmap-fu and I started my Backtrack 4 virtual machine. “Don’t worry, I’ll do a simple NMAP Ping scan…”

# nmap -sP 192.168.x.x/24

To my surprise (and shame) every single IP Address appeared listed as “UP“.

FAIL.

“This can’t be. Hmmm… Do this customer have any kind of IPS/IDS solution in place?”

“Quite probably…”

My first thought was that some kind of IPS recognized the “Ping sweep” and was answering on behalf of the clients, saying all of them are up. Security by obscurity…

I tried it again at a lower pace, using the -T Nmap parameter but I got the same results. Apparently this had something to do with some packet characteristics.

At the end we used a small windows program, Angry IP Scanner, which worked like a charm.

We didn’t have neither enough info about the network infrastructure from this customer nor time to research this funny behaviour but once back in Munich I started to play with Snort and Nmap.

Snort detects this type of scan from Nmap. Here’s an excerpt of the rule:

@ snort – icmp.rules:

alert icmp $EXTERNAL_NET any -> $HOME_NET any (msg:"ICMP PING NMAP"; dsize:0; itype:8; reference:arachnids,162; classtype:attempted-recon; sid:469; rev:3;)

Let’s see this in action.

Note: The parameter “–send-ip” specifies that I don’t want to use ARP queries as host detection method.
@ BT4 – Nmap PING sweep

root@bt:~# nmap -v -n -sP –send-ip 192.168.88.139

Starting Nmap 5.21 ( http://nmap.org ) at 2010-04-04 08:34 EDT
Initiating Ping Scan at 08:34
Scanning 192.168.88.139 [4 ports]
Completed Ping Scan at 08:34, 0.00s elapsed (1 total hosts)
Nmap scan report for 192.168.88.139
Host is up (0.0018s latency).
MAC Address: 00:0C:29:80:BB:D4 (VMware)
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.07 seconds
Raw packets sent: 4 (152B) | Rcvd: 1 (28B)

root@ubuntu:~# tail -f /var/log/snort/alert

[…]

[**] [1:469:3] ICMP PING NMAP [**]
[Classification: Attempted Information Leak] [Priority: 2]
04/04-05:34:47.622963 192.168.88.132 -> 192.168.88.139
ICMP TTL:50 TOS:0x0 ID:3270 IpLen:20 DgmLen:28
Type:8  Code:0  ID:6629   Seq:0  ECHO
[Xref => http://www.whitehats.com/info/IDS162%5D

I captured the icmp packets from Nmap with tcpdump and it confirmed my suspicion: ICMP ECHO packets sent from Nmap have a null data section :)

This is not cool (evident signature) and I thought that even when probably there was a parameter to avoid this behaviour, it would be nice to override this in the code. So I started diving into NMAP *complex* code… here is what I found.

——– Carlos in Nmap Land ———–

@ targets.cc (massping)

static void massping(Target *hostbatch[], int num_hosts, struct scan_lists *ports) {
[…]

ultra_scan(targets, ports, PING_SCAN, &group_to);
}

@ scan_engine.cc

void ultra_scan(vector<Target *> &Targets, struct scan_lists *ports, … ) {
[…]

doAnyPings(USI);

}

doAnyPings – calls sendPingProbe(USI, hss)

3347 static void sendPingProbe(UltraScanInfo *USI, HostScanStats *hss) {
3348   if (o.debugging > 1) {
3349     char tmpbuf[64];
3350     log_write(LOG_PLAIN, “Ultrascan PING SENT to %s [%s]n”, hss->target->targetipstr(),
3351               probespec2ascii(&hss->target->pingprobe, tmpbuf, sizeof(tmpbuf)));
3352   }
3353   if (hss->target->pingprobe.type == PS_CONNECTTCP) {
3354     sendConnectScanProbe(USI, hss, hss->target->pingprobe.pd.tcp.dport, 0,
3355                          hss->nextPingSeq(true));
3356   } else if (hss->target->pingprobe.type == PS_TCP || hss->target->pingprobe.type == PS_UDP
3357     || hss->target->pingprobe.type == PS_SCTP || hss->target->pingprobe.type == PS_PROTO
3358     || hss->target->pingprobe.type == PS_ICMP) {
3359     sendIPScanProbe(USI, hss, &hss->target->pingprobe, 0, hss->nextPingSeq(true));   <—————— !!!!
3360   } else if (hss->target->pingprobe.type == PS_ARP) {
3361     sendArpScanProbe(USI, hss, 0, hss->nextPingSeq(true));
3362   } else if (USI->scantype == RPC_SCAN) {
3363     assert(0); /* TODO: fill out */

[…]
3025 static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
3026                             const probespec *pspec, u8 tryno, u8 pingseq) {

[…]

3224   } else if (pspec->type == PS_ICMP) {
3225     u16 icmp_ident;
3226
3227     /* Some hosts do not respond to ICMP requests if the identifier is 0. */
3228     do {
3229       icmp_ident = get_random_u16();
3230     } while (icmp_ident == 0);
3231
3232     for(decoy = 0; decoy < o.numdecoys; decoy++) {
3233       packet = build_icmp_raw(&o.decoys[decoy], hss->target->v4hostip(),
3234                               o.ttl, ipid, IP_TOS_DEFAULT, false,
3235                               o.ipoptions, o.ipoptionslen,
3236                               0, icmp_ident, pspec->pd.icmp.type, pspec->pd.icmp.code,
3237                               o.extra_payload, o.extra_payload_length,
3238                               &packetlen);

[…]

@ tcpip.h

550 u8 *build_icmp_raw(const struct in_addr *source, const struct in_addr *victim,
551                    int ttl, u16 ipid, u8 tos, bool df,
552                    u8* ipopt, int ipoptlen,
553                    u16 seq, unsigned short id, u8 ptype, u8 pcode,
554                    char *data, u16 datalen, u32 *packetlen);

@ NmapOps.h  — were the command line parameters are parsed… If I had just started here! :)

243   extra_payload_length = 0;
244   extra_payload = NULL;

It looks like this parameter is alwasy NULL (0) as long as we don’t specify a –data-length parameter.

Finally I found:

@ nmap.cc

903         o.extra_payload_length = atoi(optarg);   <—- it parses the command line argument
904         if (o.extra_payload_length < 0) {
905           fatal(“data-length must be greater than 0”);
906         } else if (o.extra_payload_length > 0) {
907           o.extra_payload = (char *) safe_malloc(o.extra_payload_length);
908           get_random_bytes(o.extra_payload, o.extra_payload_length);
909         }

So I just did a “dirty hack” to override the parsing of the parameter and set a payload size of 40 bytes even if no parameter is passed.

901         o.debugging++;
902 //      } else if (optcmp(long_options[option_index].name, “data-length”) == 0) {
903 //      o.extra_payload_length = atoi(optarg);
904         } else if (1) {
905         o.extra_payload_length = 40;
906 //      if (o.extra_payload_length < 0) {
907 //       fatal(“data-length must be greater than 0”);
908 //      } else if (o.extra_payload_length > 0) {
909           o.extra_payload = (char *) safe_malloc(o.extra_payload_length);
910           get_random_bytes(o.extra_payload, o.extra_payload_length);
911        }
912       } else if (optcmp(long_options[option_index].name, “send-eth”) == 0) {
913         o.sendpref = PACKET_SEND_ETH_STRONG;

[…]

recompile and it’s done!

And this is what happens kids, when somebody ist bored at home and without internet access :)

Advertisements

One thought on “Snort, NMAP Ping scan and (fast) one line hacks

  1. carlos,

    good information here, helped me figure out this problem as well.

    while not as sophisticated as your resolution, you might want to check out the following thread, it explained some stuff to me about the all hosts up problem.
    http://seclists.org/nmap-dev/2010/q1/659

    the –reason option has proved very valuable to my testing.

    keep up the good posts, i am going to check out the reversing videos next!

    josh

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s