Using ICMP ping to perform layer 3 discovery
Layer 3 discovery is probably the most commonly used tool among network administrators and technicians. Layer 3 discovery uses the famous ICMP ping to identify live hosts. This recipe will demonstrate how to use the ping utility to perform layer 3 discovery on remote hosts.
Getting ready
Using ping to perform layer 3 discovery does not require a lab environment, as many systems on the Internet will reply to ICMP echo requests. However, it is highly recommended that you perform any type of network scanning exclusively in your own lab unless you are thoroughly familiar with the legal regulations imposed by any governing authorities to whom you are subject. If you wish to perform this technique within your lab, you will need to have at least one system that will respond to ICMP requests. In the examples provided, a combination of Linux and Windows systems are used. For more information on setting up systems in a local lab environment, please refer to the Installing Metasploitable2 and Installing Windows Server recipes in Chapter 1, Getting Started. Additionally, this section will require a script to be written to the filesystem, using a text editor such as VIM or Nano. For more information on writing scripts, please refer to the Using text editors (VIM and Nano) recipe in Chapter 1, Getting Started.
How to do it...
Most people who work in the IT industry are fairly familiar with the ping tool. To determine whether a host is alive using ping
, you merely need to pass an argument to the command to define the IP address that you wish to test:
root@KaliLinux:~# ping 172.16.36.135 PING 172.16.36.135 (172.16.36.135) 56(84) bytes of data. 64 bytes from 172.16.36.135: icmp_req=1 ttl=64 time=1.35 ms 64 bytes from 172.16.36.135: icmp_req=2 ttl=64 time=0.707 ms 64 bytes from 172.16.36.135: icmp_req=3 ttl=64 time=0.369 ms ^C --- 172.16.36.135 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 0.369/0.809/1.353/0.409 ms
When this command is issued, an ICMP echo request will be sent directly to the IP address provided. Several conditions must be true in order to receive a reply to this ICMP echo request. These conditions are as follows:
- The IP address tested must be assigned to a system
- The system must be alive and online
- There must be an available route from the scanning system to the target IP
- The system must be configured to respond to ICMP traffic
- There is no host-based or network firewall between the scanning system and the target IP that is configured to drop ICMP traffic
As you can see, there are a lot of variables that factor into the success of ICMP discovery. It is for this reason that ICMP can be somewhat unreliable, but unlike ARP, it is a routable protocol and can be used to discover hosts outside of the LAN. Notice that in the previous example, there is ^C
that appears in the output presented from the ping
command. This signifies that an escape sequence (specifically, Ctrl + C) was used to stop the process. Unlike Windows, the ping
command integrated into Linux operating systems will, by default, ping a target host indefinitely. However, the -c
option can be used to specify the number of ICMP requests to be sent. Using this option, the process will end gracefully once the timeout has been reached or replies have been received for each sent packet. Have a look at the following command:
root@KaliLinux:~# ping 172.16.36.135 -c 2 PING 172.16.36.135 (172.16.36.135) 56(84) bytes of data. 64 bytes from 172.16.36.135: icmp_req=1 ttl=64 time=0.611 ms 64 bytes from 172.16.36.135: icmp_req=2 ttl=64 time=0.395 ms --- 172.16.36.135 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 0.395/0.503/0.611/0.108 ms
In the same way that ARPing can be used in a bash script to cycle through multiple IPs in parallel, ping
can be used in conjunction with bash scripting to perform layer 3 discovery on multiple hosts in parallel. To write a script, we need to identify the varied responses associated with a successful and failed ping request. To do this, we should first ping a host that we know to be alive and responding to ICMP, and then follow it up with a ping request to a nonresponsive address. The following command demonstrates this:
root@KaliLinux:~# ping 74.125.137.147 -c 1 PING 74.125.137.147 (74.125.137.147) 56(84) bytes of data. 64 bytes from 74.125.137.147: icmp_seq=1 ttl=128 time=31.3 ms --- 74.125.137.147 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 31.363/31.363/31.363/0.000 ms root@KaliLinux:~# ping 83.166.169.231 -c 1 PING 83.166.169.231 (83.166.169.231) 56(84) bytes of data. --- 83.166.169.231 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms
As with the ARPing requests, the bytes from
unique string is only present in the output associated with live IP addresses, and it is also on a line that contains this address. In the same fashion, we can extract the IP address from any successful ping request using a combination of grep
and cut
:
root@KaliLinux:~# ping 74.125.137.147 -c 1 | grep "bytes from" 64 bytes from 74.125.137.147: icmp_seq=1 ttl=128 time=37.2 ms root@KaliLinux:~# ping 74.125.137.147 -c 1 | grep "bytes from" | cut -d " " -f 4 74.125.137.147: root@KaliLinux:~# ping 74.125.137.147 -c 1 | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1 74.125.137.147
By employing this task sequence in a loop that contains a range of target IP addresses, we can quickly identify live hosts that respond to ICMP echo requests. The output is a simple list of live IP addresses. An example script that uses this technique can be seen as follows:
#!/bin/bash if [ "$#" -ne 1 ]; then echo "Usage - ./ping_sweep.sh [/24 network address]" echo "Example - ./ping_sweep.sh 172.16.36.0" echo " Example will perform an ICMP ping sweep of the 172.16.36.0/24 network" exit fi prefix=$(echo $1 | cut -d '.' -f 1-3) for addr in $(seq 1 254); do ping -c 1 $prefix.$addr | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1 & done
In the provided bash script, the first line defines the location of the bash interpreter. The block of code that follows performs a test to determine whether the one argument that was expected was supplied. This is determined by evaluating whether the number of supplied arguments is not equal to 1
. If the expected argument is not supplied, the usage of the script is output, and the script exits. The usage output indicates that the script is expecting the /24
network address as an argument. The next line of code extracts the network prefix from the supplied network address. For example, if the network address supplied was 192.168.11.0
, the prefix
variable would be assigned 192.168.11
. A for
loop is then used to cycle through the values of the last octet to generate each possible IP address in the local /24
network. For each possible IP address, a single ping
command is issued. The response for each of these requests is then piped over, and then grep
is used to extract lines with the phrase, bytes from
. This will only extract lines that include the IP address of live hosts. Finally, a series of cut
functions are used to extract the IP address from that output. Notice that an ampersand is used at the end of the for
loop task, instead of a semicolon. The ampersand allows the tasks to be performed in parallel instead of in sequence. This drastically reduces the amount of time required to scan the IP range. The script can then be executed with a period and forward slash, followed by the name of the executable script:
root@KaliLinux:~# ./ping_sweep.sh Usage - ./ping_sweep.sh [/24 network address] Example - ./ping_sweep.sh 172.16.36.0 Example will perform an ICMP ping sweep of the 172.16.36.0/24 network root@KaliLinux:~# ./ping_sweep.sh 172.16.36.0 172.16.36.2 172.16.36.1 172.16.36.232 172.16.36.249
When executed without any arguments supplied, the script returns the usage. However, when executed with a network address value, the task sequence begins, and a list of live IP addresses is returned. As discussed in the previous scripts, the output of this script can also be redirected to a text file for later use. This can be done with a right-angled bracket followed by the name of the output file.
root@KaliLinux:~# ./ping_sweep.sh 172.16.36.0 > output.txt root@KaliLinux:~# ls output.txt output.txt root@KaliLinux:~# cat output.txt 172.16.36.2 172.16.36.1 172.16.36.232 172.16.36.249
In the example provided, the ls
command is used to confirm that the output file was created. The contents of this output file can be viewed by passing the filename as an argument to the cat
command.
How it works…
Ping is a well-known utility in the IT industry, and its existing functionality is already to identify live hosts. However, it was built with the intention of discovering if a single host is alive and not as a scanning tool. The bash script in this recipe essentially does the same thing as using ping on every possible IP address in a /24
CIDR range. However, rather than doing this tedious task manually, bash allows us to quickly and easily perform this task by passing the task sequence through a loop.