Friday 14 August 2020

Ping Scanner Part 3: Using a DNS Server to resolve Responsive IP Addresses

 by Vaggelis Atlasis


Previously, I built on the ping scanner by adding the code necessary for multi-processing, making the ping-sweep process faster. The next step was to implement the code for a reverse look-up, using the responsive IP addresses to resolve their hostnames.

The previous version of the code

Resolving the IP Address

To be able to access and utilise a DNS server, I imported DNS, DNSQR, UDP and RandShort from Scapy, as well as ip_address from ipaddress. I began by initialising the variable dns_server to enable the user to choose a DNS server of their choice later on in the program. 

I then defined the function resolve_IP with ip_address_to_resolve as a parameter in order to resolve a given ip_address. I globalised the variable dns_server as the program got confused and printed an error in line 13 (dst = dns_server). For the IP address to be resolved, it first needed to be reversed. I used the ip_address().reverse_pointer function to take in an IP address and return the reverse version of the function (e.g. 192.168.1.0 becomes 0.1.168.192.in-addr.arpa).

I then initialise the packet which is to be sent as a query to the server. First, for the IP protocol (line 13), we set the destination to the one of the server chosen by the user. For UDP - which is the transfer protocol DNS servers use - I set the destination port to 53, which is the one DNS has been designed to use with UDP and TCP. Finally, I use RandShort to choose a source port at random. Finally, we need to assign the details for the DNS protocol, however, we will explore them in another post later on. In line 15, I set the verbose (amount of unnecessary/excess information) to 0 and the timeout (how much to wait for a response) to 2 seconds. Next, I set the default of the hostname of the IP address to "not resolved" (why will be explained soon). 

If there is a response by the DNS server, the program checks that the response is an integer UDP response with a source port of 53. Then, the program tries to replace the previous hostname with the one returned by the DNS server. If this is not possible, the exception is passed. The hostname is then returned to the program. 

Implementing a DNS request using a function (Lines 13 and 14 should be one combined line, they were only split so that the full line would fit into the picture).

Implementing the Resolving Function in the Ping Function

The ping function remained largely unchanged. The only difference is that in line 33, with address as the value for ip_address_to_resolve. The next change is not necessary to the program but makes it more user friendly as the hostname is displayed after a responsive IP address. If there was no hostname returned by the DNS server earlier, the default message ("not resolved" is printed instead.

The updated ping function

Allowing the User to Input a DNS Server from the Command Line

Using an argument parser, the user can use -dns or --dns_server to input a string of characters corresponding to a DNS server of their choice from the command line. Should they choose not to input anything, the default value for the server is "8.8.8.8" (Google's DNS server). This argument is then assigned to the variable values. Finally, dns_server is globalised and assigned the value of values.dns_server (the .dns_server part is necessary so that only the DNS server is assigned). From there, when the function is run, the DNS server the user has inputted (or the default server) will be the DNS server used in the program. 

Parsing an argument from the command line and assigning it to the value for the dns_server (line 41 and 42 should be one line but were split so that all of the code fit in the snapshot)

Up Next 

In the next post, we will TCP scan for the open ports for each of the hosts we've identified. I will also provide an explanation on the details of the DNS protocol when it comes to packets.