Description
Kali Linux Network Scanning Cookbook will introduce you to critical scanning concepts. You will be shown techniques associated with a wide range of network scanning tasks that include discovery scanning, port scanning, service enumeration, operating system identification, vulnerability mapping, and validation of identified findings. You will learn how to utilize the arsenal of tools available in Kali Linux to conquer any network environment. You will also be shown how to identify remote services, how to assess security risks, and how various attacks are performed. This immersive guide will also encourage the creation of personally scripted tools and the development of skills required to create them.
Â
Â
Read an extract of the book
UDP scanning with Scapy
Scapyis a tool that can be used to craft and inject custom packets into a network. In this specific recipe, Scapy will be used to scan for active UDP services. This can be done by sending an empty UDP packet to destination ports and then identifying the ports that do not respond with an ICMP port-unreachable response.
Getting ready
To use Scapy to perform UDP scanning, you will need to have a remote system that is running network services over UDP. In the examples provided, an instance of Metasploitable2 is used to perform this task. Additionally, this section will require a script to be written to the filesystem using a text editor, such as VIM or Nano.
How to do it…
Using Scapy, we can quickly develop an understanding of the underlying principles behind how UDP scanning works. To positively confirm the existence of a UDP service on any given port, we will need to solicit a reply from that service. This can prove to be very difficult, as many UDP services will only reply to service-specific requests. Knowledge of any particular service can make it easier to positively identify that service; however, there are general techniques that can be used to determine, with a reasonable amount of accuracy, whether a service is running on a given UDP port. The technique that we will use with Scapy is to identify closed UDP ports with ICMP port-unreachable replies. To send a UDP request to any given port, we first need to build layers of that request. The first layer that we will need to construct is the IP layer:
root@KaliLinux:~# scapy
Welcome to Scapy (2.2.0)
>>> i = IP()
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
options
>>> i.dst = “172.16.36.135”
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.36.180
dst= 172.16.36.135
options
To build the IP layer of our request, we need to assign the IP object to the variable i. By calling the display function, we can identify the attribute configurations for the object. By default, both the sending and receiving addresses are set to the loopback address, 127.0.0.1. These values can be modified by changing the destination address, by setting i.dst to be equal to the string value of the address that we wish to scan. On calling the display function again, we see that not only has the destination address been updated, but Scapy also automatically updates the source IP address to the address associated with the default interface. Now that we have constructed the IP layer of the request, we can proceed to the UDP layer:
>>> u = UDP()
>>> u.display()
###[ UDP ]###
sport= domain
dport= domain
len= None
chksum= None
>>> u.dport
53
To build the UDP layer of our request, we use the same technique that we used for the IP layer. In the example provided, the UDP object was assigned to the u variable. As mentioned previously, the default configurations can be identified by calling the display function. Here, we can see that the default value for both the source and destination ports are listed as domain. As you might likely suspect, this is to indicate the Domain Name System (DNS) service associated with port 53. DNS is a common service that can often be discovered on networked systems. To confirm this, one can call the value directly by referencing the variable name and attribute. This can then be modified by setting the attribute equal to the new port destination value as follows:
>>> u.dport = 123
>>> u.display()
###[ UDP ]###
sport= domain
dport= ntp
len= None
chksum= None
In the preceding example, the destination port is set to 123, which is the Network Time Protocol (NTP) port. Now that we have created both the IP and UDP layers, we need to construct the request by stacking these layers:
>>> request = (i/u)
>>> request.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= None
src= 172.16.36.180
dst= 172.16.36.135
options
###[ UDP ]###
sport= domain
dport= ntp
len= None
chksum= None
We can stack the IP and UDP layers by separating the variables with a forward slash. These layers can then be set equal to a new variable that will represent the entire request. We can then call the display function to view the configurations for the request. Once the request has been built, it can be passed to the sr1 function so that we can analyze the response:
>>> response = sr1(request)
Begin emission:
……Finished to send 1 packets.
….*
Received 11 packets, got 1 answers, remaining 0 packets
>>> response.display()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0xc0
len= 56
id= 63687
flags=
frag= 0L
ttl= 64
proto= icmp
chksum= 0xdfe1
src= 172.16.36.135
dst= 172.16.36.180
options
###[ ICMP ]###
type= dest-unreach
code= port-unreachable
chksum= 0x9e72
unused= 0
###[ IP in ICMP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 28
id= 1
flags=
frag= 0L
ttl= 64
proto= udp
chksum= 0xd974
src= 172.16.36.180
dst= 172.16.36.135
options
###[ UDP in ICMP ]###
sport= domain
dport= ntp
len= 8
chksum= 0x5dd2
This same request can be performed without independently building and stacking each layer. Instead, we can use a single, one-line command by calling the functions directly and passing them the appropriate arguments as follows:
>>> sr1(IP(dst=”172.16.36.135″)/UDP(dport=123))
..Begin emission:
…*Finished to send 1 packets.
Received 6 packets, got 1 answers, remaining 0 packets
Note that the response for these requests includes an ICMP packet that has type indicating that the host is unreachable and code indicating that the port is unreachable. This response is commonly returned if the UDP port is closed. Now, we should attempt to modify the request so that it is sent to a destination port that corresponds to an actual service on the remote system. To do this, we change the destination port back to port 53 and then send the request again, as follows:
>>> response = sr1(IP(dst=”172.16.36.135″)/UDP(dport=53),timeout=1,verbose=1)
Begin emission:
Finished to send 1 packets.
Received 8 packets, got 0 answers, remaining 1 packets
When the same request is sent to an actual service, no reply is received. This is because the DNS service running on the system’s UDP port 53 will only respond to service-specific requests. Knowledge of this discrepancy can be used to scan for ICMP host-unreachable replies, and we can then identify potential services by flagging the nonresponsive ports:
#!/usr/bin/python
import logging
logging.getLogger(“scapy.runtime”).setLevel(logging.ERROR)
from scapy.all import *
import time
import sys
if len(sys.argv) != 4:
print “Usage – ./udp_scan.py [Target-IP] [First Port] [Last Port]”
print “Example – ./udp_scan.py 10.0.0.5 1 100”
print “Example will UDP port scan ports 1 through 100 on 10.0.0.5”
sys.exit()
ip = sys.argv[1]
start = int(sys.argv[2])
end = int(sys.argv[3])
for port in range(start,end):
ans = sr1(IP(dst=ip)/UDP(dport=port),timeout=5,verbose=0)
time.sleep(1)
if ans == None:
print port
else:
pass
The provided Python script sends a UDP request to each of the first hundred ports in sequence. In the case that no response is received, the port is identified as being open. By running this script, we can identify all of the ports that don’t return an ICMP host-unreachable reply:
root@KaliLinux:~# chmod 777 udp_scan.py
root@KaliLinux:~# ./udp_scan.py
Usage – ./udp_scan.py [Target-IP] [First Port] [Last Port]
Example – ./udp_scan.py 10.0.0.5 1 100
Example will UDP port scan ports 1 through 100 on 10.0.0.5
root@KaliLinux:~ # ./udp_scan.py 172.16.36.135 1 100
53
68
69
A timeout of 5 seconds is used to adjust for latent responses that result from ICMP hostunreachable rate limiting. Even with this rather large response acceptance window, scanning in this fashion can still be unreliable at times. It is for this reason that UDP probing scans are often a more effective alternative.
How it works…
In this recipe, UDP scanning is performed by identifying the ports that do not respond with ICMP port-unreachable responses. This process can be highly time consuming as ICMP port-unreachable responses are often throttled. It can also, at times, be an unreliable approach as some systems do not generate these responses, and ICMP is often filtered by firewalls. An alternative approach is to use service-specific probes that attempt to solicit a positive response.