1
votes

I'm a beginner, but I am trying to make a network scanning utility that can filter MAC addresses for a specific need; The company I work for has network connected devices that are assigned MAC addresses based on serial numbers. I have found that the first 6 digits of the MAC address are the indicator of our brand. This I have made into a str below. The 4th field of the MAC address is a small range of constant numbers that indicate the model of the device. I have these ready but its essentially a few numbers like '14', '17,' etc.

I'm struggling to figure out a way to "filter" the MAC addresses that are retrieved from the scan and label them depending on the fields of the address. OR even better, only print IP's and Mac addresses that match the startswith(mac_key) and label the remaining objects according to their MAC address 4th field [9:11].

With a lot of reading and help, I've got this so far:

    #!/usr/bin/env python
from scapy.all import ARP, Ether, srp
import socket
# importing main functions from Scapy and Socket

mac_key = '04:24:2f'
# Target value for first three fields of MAC address

hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
target_ip = ("192.168.{}.0/24".format(IPAddr[6]))
# Assigning index value for third section of IP address
# To make third section of target_ip a variable determined by host
# "/24" denotes IP address spectrum for the ARP packet destination

arp = ARP (pdst=target_ip)
# Creating ARP packet assigned to "target_ip"

ether = Ether(dst="ff:ff:ff:ff:ff:ff")
# Creating Ether broadcast packet
# ff:ff:ff:ff:ff:ff MAC address indicates broadcasting

packet = ether/arp
# Stacking

result = srp(packet, timeout=5, verbose=3)[0]
# Defining result with timeout parameter

clients= []
# Client list to be finished below

for sent, received in result:
    clients.append({'ip': received.psrc, 'mac': received.hwsrc})
    # For each response, append ip and mac address to 'clients' list

print("Devices On This Network:")
print("IP" + " "*18+"MAC")
# Print table of accumulated data

for client in clients:
    print("{:24}    {}".format(client['ip'], client['mac'].startswith(mac_key)))
# Printing IP addresses and assosciated MACs from clients list
# With bool checking against mac_key

The image below is the result in terminal; the idea would be to only print lines that show a value of TRUE and to add a label based on field [9:11] of the MAC address ex: "Network Device Pro" where the TRUE bool appears, and completely omit the line where a FALSE bool is triggered.

EDIT: Well, I've turned this into a blog at this point. I managed to do exactly what I wanted to do, and I will provide the code below for anyone who is trying to do something similar. I am open to any suggestions to make this more 'pythonic' and improve performance / syntax. I do have one question for anyone who can offer some advice; I want to loop this code and append information to the rendered list and include a user input kill switch to finish it off. This way if a packet does not reach on the first scan, it will still be added to the list. Bonus points if you can offer a suggestion for this, as well as a way to remove entries that don't respond after a consecutive number of loops!!

#!/usr/bin/env python3
# coding: utf-8
#
#
#//////////////////////////////////
#----------------------------------
# ippy Network Scanning Utility
#   ~ Daniel Johnston 2020 ~
#----------------------------------
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
#
#
print("Initializing Scan...")
from scapy.all import ARP, Ether, srp
import socket
# importing main functions from Scapy and Socket

mac_key = ('00:25:2f')
# Target value for first three fields of MAC address (Brand Identifier)

MTU_key = ('13','15','16','17')
GW_key = ('21')
ECC_key = ('26')

#------serial numbers)------#

#('13','15','16','17','21','26'#

#---------LEGEND------------#
#serial numbers[0:3] = 'MTU'
#serial numbers[4] = 'Gateway'
#serial numbers[5] = 'ECC'
#---------------------------#

hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
target_ip = ("192.168.{}.0/24".format(IPAddr[6]))
# Assigning index value for third section of IP address
# To make third section of target_ip a variable determined by host
# "/24" denotes IP address spectrum for the ARP packet destination

def devsub(x):
        if x.startswith(MTU_key, 9, 11):
            print("{}   {}".format('MTU', client['ip'],))
        if x.startswith(GW_key, 9, 11):
            print("{}   {}".format('Gateway', client['ip'],))
        if x.startswith(ECC_key, 9, 11):
            print("{}   {}".format('ECC', client['ip'],))
# Defining function to print associated IP addresses, of MAC address    
# Matches(done later), and assigning a device name based on     
# index[9:11] of MAC

arp = ARP (pdst=target_ip)
# Creating ARP packet assigned to "target_ip"

ether = Ether(dst="ff:ff:ff:ff:ff:ff")
# Creating Ether broadcast packet
# ff:ff:ff:ff:ff:ff MAC address indicates broadcasting

packet = ether/arp
# Stacking

result = srp(packet, timeout=5, verbose=3)[0]
# Defining result with timeout parameter

clients= []
# Client list to be finished below

for sent, received in result:
    clients.append({'ip': received.psrc, 'mac': received.hwsrc})
# For each response, append ip and mac address to 'clients' list

print("~"*20)
print("-"*20)
print("Devices On This Network:")
print("-"*20)
print("Device" + " " * 4 + "IP Address")
#Text formatting
for client in clients:
    if client['mac'].startswith(mac_key):
        devsub(client['mac'])
#Running primary filter to only include MAC addresses that pass
# .startswith(mac_key) into devsub function to print device name 
# Associated with MAC[9:11] variables, and the appropriate IP address

So this is the current output, with 1 matching device

enter image description here

1
You should add expected output and a sample input file to your question as they sound relevantRoss Jacobs
I scrapped the idea of an input file for now, as I found a workaround of sorts.I have updated the question, code, and added current output as well as a better explanation of desired output. Thanks!Daniel Johnston
I would point out that assuming /24 for all networks is completely incorrect, and second, this will not scale for IPv6 where a standard IPv6 /64 network scan even at 1,000,000 scans per second (not possible) would take more than 584,542 years!Ron Maupin
Thanks Ron, is there any other way?Daniel Johnston

1 Answers

0
votes

It makes more sense to use scapy builtins than rewrite them. In this case, use arping. To save yourself time as you work with scapy (like here), you should read the manual.

This will print the 4th octets to a file:

from scapy.all import arping

local_devices = arping("192.168.1.0/24")
local_macs = [device[1].src for device in local_devices[0]]
fourth_octets = [mac[9:11] for mac in local_macs]
with open("fourth_octets.txt", "w") as f:
     f.write("\n".join(fourth_octet))

Explanation in Scapy Interpreter

>>> # Get local devices. Four have been found, and 252 provided no answer.
>>> local_devices = arping("192.168.128.0/24")
>>> local_devices
(<ARPing: TCP:0 UDP:0 ICMP:0 Other:4>,
 <Unanswered: TCP:0 UDP:0 ICMP:0 Other:252>)

>>> # Get the mac addrs of each of the ARP responses with a list comprehension
>>> local_macs = [device[1].src for device in local_devices[0]]
>>> local_macs
['e0:55:3d:4d:e4:58',
 '00:18:0a:27:26:ee',
 'e0:55:3d:d2:0a:12',
 '38:53:9c:08:b9:9f']

>>> # Get the fourth octet by string position in the macs
>>> fourth_octets = [mac[9:11] for mac in local_macs]
>>> fourth_octets
['4d', '27', 'd2', '08']

>>> # Write to the file with the fourth octets, each on a line
>>> with open("fourth_octets.txt", "w") as f:
     f.write("\n".join(fourth_octets))

>>> # Verify that the text file looks as expected
>>> with open("fourth_octets.txt") as f:
     print(f.read())

4d
27
d2
08