Table of Contents
Previous Section Next Section

0x360 Port Scanning

Port scanning is a way of figuring out which ports are listening and accepting connections. Because most services run on standard, documented ports, this information can be used to determine which services are running. The simplest form of port scanning involves trying to open TCP connections to every possible port on the target system. While this is effective, it's also noisy and detectable. Also, when connections are established, services will normally log the IP address. To avoid this, several clever techniques have been invented to avoid detection.

0x361 Stealth SYN Scan

A SYN scan is also sometimes called a half-open scan. This is because it doesn't actually open a full TCP connection. Recall the TCP/IP handshake: When a full connection is made, first a SYN packet is sent, then a SYN/ACK packet is sent back, and finally an ACK packet is returned to complete the handshake and open the connection. A SYN scan doesn't complete the handshake, so a full connection is never opened. Instead, only the initial SYN packet is sent, and the response is examined. If a SYN/ACK packet is received in response, that port must be accepting connections. This is recorded, and a RST packet is sent to tear down the connection to prevent the service from accidentally being DoSed.

0x362 FIN, X-mas, and Null Scans

In response to SYN scanning, new tools to detect and log half-open connections were created. So, yet another collection of techniques for stealth port scanning evolved: FIN, X-mas, and Null scans. These all involve sending a nonsensical packet to every port on the target system. If a port is listening, these packets just get ignored. However, if the port is closed and the implementation follows protocol (RFC 793), a RST packet will be sent. This difference can be used to detect which ports are accepting connections, without actually opening any connections.

The FIN scan sends a FIN packet, the X-mas scan sends a packet with FIN, URG, and PUSH turned on (named because the flags are lit up like a Christmas tree), and the Null scan sends a packet with no TCP flags set. While these types of scans are stealthier, they can also be unreliable. For instance, Microsoft's implementation of TCP doesn't send RST packets like it should, making this form of scanning ineffective.

0x363 Spoofing Decoys

Another way to avoid detection is to hide among several decoys. This technique simply spoofs connections from various decoy IP addresses in between each real port-scanning connection. The responses from the spoofed connections aren't needed, because they are simply misleads. However the spoofed decoy addresses must use real IP addresses of live hosts; otherwise the target may be accidentally be SYN flooded.

0x364 Idle Scanning

Idle scanning is a way to scan a target using spoofed packets from an idle host, by observing changes in the idle host. The attacker needs to find a usable idle host that is not sending or receiving any other network traffic and has a TCP implementation that produces predictable IP IDs that change by a known increment with each packet. IP IDs are meant to be unique per packet per session, and they are commonly incremented by 1 or 254 (depending on byte ordering) on Windows 95 and 2000, respectively. Predictable IP IDs have never really been considered a security risk, and idle scanning takes advantage of this misconception.

First the attacker gets the current IP ID of the idle host by contacting it with a SYN packet or an unsolicited SYN/ACK packet, and observing the IP ID of the response. By repeating this process a couple more times, the increment that the IP ID changes with each packet can be determined.

Then the attacker sends a spoofed SYN packet with the idle host's IP address to a port on the target machine. One of two things will happen, depending on whether that port on the victim machine is listening:

  • If that port is listening, a SYN/ACK packet will be sent back to the idle host. But because the idle host didn't actually send out the initial SYN packet, this response appears to be unsolicited to the idle host, and it responds by sending back a RST packet.

  • If that port isn't listening, the target machine will send a RST packet back to the idle host, which requires no response.

At this point, the attacker contacts the idle host again to determine how much the IP ID has incremented. If it has only incremented by one interval, no other packets were sent out by the idle host between the two checks. This implies that the port on the target machine is closed. If the IP ID has incremented by two intervals, one packet, presumably a RST packet, was sent out by the idle machine between the checks. This implies that the port on the target machine is open.

The steps are illustrated here for both possible outcomes:

Click To expand

Of course, if the idle host isn't truly idle, the results will be skewed. If there is light traffic on the idle host, multiple packets can be sent for each port. If 20 packets are sent, then a change of 20 incremental steps should be seen for an open port, and none for a closed port. Even if there is light traffic, such as one or two non–scan-related packets on the idle host, this difference is large enough that it can still be detected.

If this technique is used properly on an idle host that doesn't have any logging capabilities, the attacker can scan any target without ever revealing her IP address.

0x365 Proactive Defense (Shroud)

Port scans are often used to profile systems before they are attacked. Knowing what ports are open allows an attacker to determine which services can be attacked. Many IDSs offer methods to detect port scans, but by then the information has already been leaked. While writing this chapter, I wondered if it were possible to prevent port scans before they actually happened. Hacking really is all about coming up with new ideas, so a simple, newly developed method for proactive port-scanning defense will be presented here.

First of all, the FIN, Null, and X-mas scans can be prevented by a simple kernel modification. If the kernel never sends reset packets, these scans will turn up nothing. The following output uses grep to find the kernel code responsible for sending reset packets.

# grep -n -A 12 "void.*send_reset" /usr/src/linux/net/ipv4/tcp_ipv4.c
1161:static void tcp_v4_send_reset(struct sk_buff *skb)
1162-{
1163-   struct tcphdr *th = skb->h.th;
1164-   struct tcphdr rth;
1165-   struct ip_reply_arg arg;
1166-
1167-   return; // Modification: Never send RST, always return.
1168-
1169-   /* Never send a reset in response to a reset. */
1170-   if (th->rst)
1171-   return;
1172-
1173-   if (((struct rtable*)skb->dst)->rt_type != RTN_LOCAL)

By adding the return command (shown above in bold), the tcp_v4_send_reset() kernel function will simply return instead of doing anything. After the kernel is recompiled, the result is a kernel that doesn't send out reset packets, avoiding information leakage.


FIN scan before the kernel modification:

# nmap -vvv -sF 192.168.0.189

Starting nmap V. 3.00 ( www.insecure.org/nmap/ )
Host (192.168.0.189) appears to be up ... good.
Initiating FIN Scan against (192.168.0.189)
The FIN Scan took 17 seconds to scan 1601 ports.
Adding open port 22/tcp
Interesting ports on (192.168.0.189):
(The 1600 ports scanned but not shown below are in state: closed)
Port       State       Service
22/tcp     open        ssh

Nmap run completed -- 1 IP address (1 host up) scanned in 17 seconds
#

FIN scan after the kernel modification:

# nmap -sF 192.168.0.189

Starting nmap V. 3.00 ( www.insecure.org/nmap/ )
All 1601 scanned ports on (192.168.0.189) are: filtered

Nmap run completed -- 1 IP address (1 host up) scanned in 100 seconds
#

This works fine for scans that rely on RST packets, but preventing information leakage with SYN scans and full-connect scans is a bit more difficult. In order to maintain functionality, open ports have to respond with SYN/ACK packets, but if all of the closed ports also responded with SYN/ACK packets, the amount of useful information an attacker could retrieve from port scans would be minimized. Simply opening every port would cause a major performance hit, though, which isn't desirable. Ideally, this should all be done without using the TCP stack. That sounds like a job for a nemesis script:

File: shroud.sh

#!/bin/sh
HOST="192.168.0.189"
/usr/sbin/tcpdump -e -S -n -p -l "(tcp[13] == 2) and (dst host $HOST) and !(dst
port 22)" | /bin/awk '{
# Output numbers as unsigned
  CONVFMT="%u";
# Seed the randomizer
  srand();

# Parse the tcpdump input for packet information
  dst_mac = $2;
  src_mac = $3;
  split($6, dst, ".");
  split($8, src, ".");
  src_ip = src[1]"."src[2]"."src[3]"."src[4];
  dst_ip = dst[1]"."dst[2]"."dst[3]"."dst[4];
  src_port = substr(src[5], 1, length(src[5])-1);
  dst_port = dst[5];

# Increment the received seq number for the new ack number
  ack_num = substr($10,1,index($10,":")-1)+1;
# Generate a random seq number
  seq_num = rand() * 4294967296;

# Feed all this information to nemesis
  exec_string = "nemesis tcp -v -fS -fA -S "src_ip" -x "src_port" -H "src_mac" -D
"dst_ip" -y "dst_port" -M "dst_mac" -s "seq_num" -a "ack_num;

# Display some helpful debugging info.. input vs. output
  print "[in] "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "$10;
  print "[out] "exec_string;

# Inject the packet with nemesis
  system(exec_string);
}'

When running this script, make sure that the HOST variable is set to the current IP address of your host.

The 13th octet is used for a tcpdump filter again, this time only accepting packets that are destined for the given host IP on any port, except for 22, and that only have the SYN flag on. This will pick up SYN scan attempts, full-connect scan attempts, and any other type of connection attempt. Then the packet information is parsed through awk, and fed into nemesis to craft a realistic-looking SYN/ACK response packet. Port 22 must be avoided, because ssh is already responding on that port. All of this is done without using the TCP stack.

With the shroud script running, a telnet attempt will appear to connect even though the host machine isn't even listening to the traffic, as shown here:

From overdose @ 192.168.0.193:

overdose$ telnet 192.168.0.189 12345
Trying 192.168.0.189...
Connected to 192.168.0.189.
Escape character is '^]'.
^]
telnet> q
Connection closed.
overdose$

The shroud.sh script running on 192.168.0.189:

# ./shroud.sh
tcpdump: listening on eth1
[in] 14:07:09.793997 0:0:ad:d1:c7:ed 0:2:2d:4:93:e4 0800 74: 192.168.0.193.32837 >
192.168.0.189.12345: S 2071082535:2071082535(0)
[out] nemesis tcp -v -fS -fA -S 192.168.0.189 -x 12345 -H 0:2:2d:4:93:e4 -D
192.168.0.193 -y 32837 -M 0:0:ad:d1:c7:ed -s 979061690 -a 2071082536

TCP Packet Injection -=- The NEMESIS Project Version 1.4beta3 (Build 22)

                [MAC] 00:02:2D:04:93:E4 > 00:00:AD:D1:C7:ED
      [Ethernet type] IP (0x0800)

                 [IP] 192.168.0.189 > 192.168.0.193
              [IP ID] 2678
           [IP Proto] TCP (6)
             [IP TTL] 255
             [IP TOS] 00
     [IP Frag offset] 0000
      [IP Frag flags]

          [TCP Ports] 12345 > 32837
          [TCP Flags] SYN ACK
 [TCP Urgent Pointer] 0
    [TCP Window Size] 4096
     [TCP Ack number] 2071082536
     [TCP Seq number] 979061690

Wrote 54 byte TCP packet through linktype DLT_EN10MB.

TCP Packet Injected

Now that the script appears to be working properly, any port-scanning methods involving SYN packets should be fooled into thinking that every possible port is open.

overdose# nmap -sS 192.168.0.189

Starting nmap V. 3.00 ( www.insecure.org/nmap/ )
Interesting ports on (192.168.0.189):
Port        State    Service
1/tcp       open     tcpmux
2/tcp       open     compressnet
3/tcp       open     compressnet
4/tcp       open     unknown
5/tcp       open     rje
6/tcp       open     unknown
7/tcp       open     echo
8/tcp       open     unknown
9/tcp       open     discard
10/tcp      open     unknown
11/tcp      open     systat
12/tcp      open     unknown
13/tcp      open     daytime
14/tcp      open     unknown
15/tcp      open     netstat
16/tcp      open     unknown
17/tcp      open     qotd
18/tcp      open     msp
19/tcp      open     chargen
20/tcp      open     ftp-data
21/tcp      open     ftp
22/tcp      open     ssh
23/tcp      open     telnet
24/tcp      open     priv-mail
25/tcp      open     smtp

[ output trimmed ]

32780/tcp   open   sometimes-rpc23
32786/tcp   open   sometimes-rpc25
32787/tcp   open   sometimes-rpc27
43188/tcp   open   reachout
44442/tcp   open   coldfusion-auth
44443/tcp   open   coldfusion-auth
47557/tcp   open   dbbrowse
49400/tcp   open   compaqdiag
54320/tcp   open   bo2k
61439/tcp   open   netprowler-manager
61440/tcp   open   netprowler-manager2
61441/tcp   open   netprowler-sensor
65301/tcp   open   pcanywhere

Nmap run completed -- 1 IP address (1 host up) scanned in 37 seconds
overdose#

The only service that is actually running is ssh on port 22, but it is hidden in a sea of false positives. A dedicated attacker could simply telnet to every port to check the banners, but this technique could easily be expanded to spoof banners also. In fact, let's do that right now.

The client machine will respond to the spoofed SYN/ACK with a single ACK packet. This packet will always increment the sequence number by exactly one, so the proper response packet containing the banner can actually be predicted, generated, and sent to the client machine before that machine can even generate the ACK response. The banner response packet will have the ACK and PSH flags turned on, to match normal banner packets. Interestingly, both packets can be generated and sent out without even caring about the ACK response from the client. This means the script doesn't have to keep track of connection states, and instead the client's TCP stack will sort out the packets.

The modified shroud script looks like this:

File: shroud2.sh

#!/bin/sh
HOST="192.168.0.189"
/usr/sbin/tcpdump -e -S -n -p -l "(tcp[13] == 2) and (dst host $HOST)" | /bin/awk
'{
# Output numbers as unsigned
  CONVFMT="%u";

# Seed the randomizer
  srand();

# Parse the tcpdump input for packet information
  dst_mac = $2;
  src_mac = $3;
  split($6, dst, "."); split($8, src, ".");
  src_ip = src[1]"."src[2]"."src[3]"."src[4];
  dst_ip = dst[1]"."dst[2]"."dst[3]"."dst[4];
  src_port = substr(src[5], 1, length(src[5])-1);
  dst_port = dst[5];

# Increment the received seq number for the new ack number
  ack_num = substr($10,1,index($10,":")-1)+1;
# Generate a random seq number
  seq_num = rand() * 4294967296;

# Precalculate the sequence number for the next packet
  seq_num2 = seq_num + 1;

# Feed all this information to nemesis
  exec_string = "nemesis tcp -fS -fA -S "src_ip" -x "src_port" -H "src_mac" -D
"dst_ip" -y "dst_port" -M "dst_mac" -s "seq_num" -a "ack_num;

# Display some helpful debugging info.. input vs. output
  print "[in] "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "$10;
  print "[out] "exec_string;

# Inject the packet with nemesis
  system(exec_string);

# Do it again to craft the second packet, this time ACK/PSH with a banner
  exec_string = "nemesis tcp -v -fP -fA -S "src_ip" -x "src_port" -H "src_mac" -D
"dst_ip" -y "dst_port" -M "dst_mac" -s "seq_num2" -a "ack_num" -P banner";

# Display some helpful debugging info..
  print "[out2] "exec_string;

# Inject the second packet with nemesis
  system(exec_string);
}'

The payload of the banner packet will be pulled from a file called banner. Just to make things extra confusing for the attacker, this can be made to look exactly like the valid ssh banner. The following output looks at a normal ssh banner and puts a similar-looking banner in the banner data file. Again, when running this script, remember to set the HOST variable to your current host's IP.

On 192.168.0.189:

tetsuo# telnet 127.0.0.1 22
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
SSH-1.99-OpenSSH_3.5p1
^]
telnet> quit
Connection closed.
tetsuo# printf "SSH-1.99-OpenSSH_3.5p1\n\r" > banner
tetsuo# ./shroud2.sh
tcpdump: listening on eth1
[in] 14:41:12.931803 0:0:ad:d1:c7:ed 0:2:2d:4:93:e4 0800 74: 192.168.0.193.32843 >
192.168.0.189.12345: S 4226290404:4226290404(0)
[out] nemesis tcp -fS -fA -S 192.168.0.189 -x 12345 -H 0:2:2d:4:93:e4 -D
192.168.0.193 -y 32843 -M 0:0:ad:d1:c7:ed -s 1943811492 -a 4226290405

TCP Packet Injected
[out2] nemesis tcp -v -fP -fA -S 192.168.0.189 -x 12345 -H 0:2:2d:4:93:e4 -D
192.168.0.193 -y 32843 -M 0:0:ad:d1:c7:ed -s 1943811493 -a 4226290405 -P banner
TCP Packet Injection -=- The NEMESIS Project Version 1.4beta3 (Build 22)

                [MAC] 00:02:2D:04:93:E4 > 00:00:AD:D1:C7:ED
      [Ethernet type] IP (0x0800)

                 [IP] 192.168.0.189 > 192.168.0.193
              [IP ID] 23711
           [IP Proto] TCP (6)
             [IP TTL] 255
             [IP TOS] 00
     [IP Frag offset] 0000
      [IP Frag flags]

          [TCP Ports] 12345 > 32843
          [TCP Flags] ACK PSH
 [TCP Urgent Pointer] 0
    [TCP Window Size] 4096
     [TCP Ack number] 4226290405

Wrote 78 byte TCP packet through linktype DLT_EN10MB.

TCP Packet Injected

From another machine (overdose), it appears that a valid connection to a ssh server has occurred.

From overdose @ 192.168.0.193:

overdose$ telnet 192.168.0.189 12345
Trying 192.168.0.189...
Connected to 192.168.0.189.
Escape character is '^]'.
SSH-1.99-OpenSSH_3.5p1

Further variations could be created to randomly choose from a library of various banners or to send out a sequence of menacing ANSI sequences. Imagination is a wonderful thing.

Of course, there are also ways to get around a technique like this. I can think of at least one way right now. Can you?


Table of Contents
Previous Section Next Section