Layer 4 discovery - TCP and UDP

To verify that an RST response is received from a live host, we can use Scapy to send a TCP ACK packet to a known live host. In the example provided, the ACK packet will be sent to the TCP destination port 80. This port is commonly used to run HTTP web services. The host used in the demonstration currently has an Apache service running on this port.

  1. To do this, we need to build each of the layers of our request. The first layer to be built is the IP layer. Have a look at the following command:
  1. Here, we have initialized the i variable as an IP object and then reconfigured the standard configurations to set the destination address to the IP address of our target server. Notice that the source IP address is automatically updated when any IP address other than the loopback address is provided for the destination address. The next layer we need to build is our TCP layer. This can be seen in the commands that follow:
  1. Here, we have initialized the t variable as a TCP object. Notice that the default configurations for the object already have the destination port set to HTTP or port 80. Here, we only needed to change the TCP flags from SYN (S) to ACK (A). Now, the stack can be built by separating each of the layers with a forward slash, as seen in the following commands:
  1. Here, we have set the entire request stack equal to the request variable. Now, the request can be sent across the wire with the send and receive function, and then the response can be evaluated to determine the status of the target address:
  1. Notice that the remote system responds with a TCP packet that has the RST flag set. This is indicated by the R value assigned to the flags attribute. The entire process of stacking the request and sending and receiving the response can be compressed into a single command by calling the functions directly:
  1. Now that we have identified the response associated with an ACK packet sent to an open port on a live host, let's attempt to send a similar request to a closed port on a live system and identify whether there is any variation in response:
  1. In this request, the destination TCP port was changed from the default port 80 to the port 1111 (a port on which no service is running). Notice that the response that is returned from both an open port and a closed port on a live system is the same. Regardless of whether this is a service actively running on the scanned port, a live system will return an RST response. Additionally, it should be noted that if a similar scan is sent to an IP address that is not associated with a live system, no response will be returned. This can be verified by modifying the destination IP address in the request to one that is not associated with an actual system on the network:
  1. So, in review, we discovered that an ACK packet sent to a live host on any port, regardless of the port status, will return an RST packet, but no response will be received from an IP if no live host is associated with it. This is excellent news because it means that we can perform a discovery scan on a large number of systems by only interacting with a single port on each system. Using Scapy in conjunction with Python, we can quickly loop through all of the addresses in a /24 network range and send a single ACK packet to only one TCP port on each system. By evaluating the response returned by each host, we can easily output a list of live IP addresses:
        #!/usr/bin/python

import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *

if len(sys.argv) != 2:
print "Usage - ./ACK_Ping.py [/24 network address]"
print "Example - ./ACK_Ping.py 172.16.36.0"
print "Example will perform a TCP ACK ping scan
of the 172.16.36.0/24 range"
sys.exit()

address = str(sys.argv[1])
prefix = address.split('.')[0] + '.' + address.split('.')[1]
+ '.' + address.split('.')[2] + '.'

for addr in range(1,254):
response = sr1(IP(dst=prefix+str(addr))/
TCP(dport=80,flags='A'),timeout=1,verbose=0)
try:
if int(response[TCP].flags) == 4:
print "172.16.36."+str(addr)
except:
pass
  1. The example script that is provided is fairly simple. While looping through each of the possible values for the last octet in the IP address, the ACK packet is sent to the TCP port 80, and the response is evaluated to determine whether the integer conversion of the TCP flag within the response has the value of 4 (the value associated with a solitary RST flag). If the packet has an RST flag, the script outputs the IP address of the system that returned the response. If no response is received, Python is unable to test the value of the response variable as no value is assigned to it. As such, an exception will occur if no response is returned. If an exception is returned, the script will then pass. The resulting output is a list of live target IP addresses. This script can be executed using a period and forward slash, followed by the name of the executable script:
  1. Similar discovery methods can be used to perform layer 4 discovery using the UDP protocol. To determine whether we can discover a host using the UDP protocol, we need to determine how to trigger a response from any live host with UDP, regardless of whether the system has a service running on the UDP port. To attempt this, we will first build our request stack in Scapy:
  1. Notice that the default source and destination port for the UDP object is DNS. This is a commonly used service that can be used to resolve domain names to IP addresses. Sending the request as it is will prove to be of very little help in determining whether the IP address is associated with a live host. An example of sending this request can be seen in the following command:
  1. Despite the fact that the host associated with the destination IP address is alive, we receive no response. Ironically, the lack of response is actually due to the fact that the DNS service is in use on the target system. Despite what you might naturally think, it can sometimes be more effective to attempt to identify hosts by probing UDP ports that are not running services, assuming that ICMP traffic is not blocked by a firewall. This is because live services are often configured to only respond to requests that contain specific content. Now, we will attempt to send the same request to a different UDP port that is not in use:
  1. By changing the request destination to port 123 and then resending it, we now receive a response indicating that the destination port is unreachable. If you examine the source IP address of this response, you can see that it was sent from the host to which the original request was sent. This response then confirms that the host at the original destination IP address is alive. Unfortunately, a response is not always returned in these circumstances. The effectiveness of this technique largely depends on the systems that you are probing and their configurations. It is because of this that UDP discovery is often more difficult to perform than TCP discovery. It is never as easy as just sending a TCP packet with a single flag lit up. In the case that services do exist, service-specific probes are often needed.

Fortunately, there are a variety of fairly complex UDP-scanning tools that can employ a range of UDP requests and service-specific probes to determine whether a live host is associated with any given IP address.