Network Scanning With Scapy in Python by Zhang Zeyu Python in Plain English
Network Scanning With Scapy in Python by Zhang Zeyu Python in Plain English
Search
Listen Share
1 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
Prerequisites
You should know basic Python. Other than that, not much! I will be writing on
some basic network theory before getting into the actual code, so if you already
know this stuff, feel free to skip ahead!
For the purpose of this tutorial, we will only concern ourselves with the ARP
2 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
As you might have noticed, as ARP is a standalone protocol, anyone can send an
ARP request at any time to learn about the devices on the network through ARP
3 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
replies. We will use Scapy to scan the network using ARP requests and create a
list of IP address to MAC address mappings.
TCP uses port numbers to differentiate between different applications on the same
device. For example, if I am running both Firefox and Chrome on my computer,
the OS uses port numbers to distinguish between the two applications so that
webpages meant for Chrome don’t show up on Firefox.
When Host P wishes to connect to Host Q, it will send a SYN packet to Host Q. If
Host Q is listening on the target port and willing to accept a new connection, it
will reply with a SYN+ACK packet. To establish the connection, Host P sends a
final ACK packet.
Using Scapy, we will send SYN packets to a range of port numbers, listen for
SYN+ACK replies, and hence determine which ports are open.
Scapy
Scapy is a packet manipulation tool written in Python. If you haven’t already, you
4 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
$ python3
...
>>> from scapy.all import *
>>> Ether()
<Ether |>
We have created an Ethernet frame. This corresponds to the data link layer (Layer
2) of the OSI model. If we don’t pass in any parameters, default values will be
used.
>>> p = Ether(dst='ff:ff:ff:ff:ff:ff')
>>> p.show()
###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = d0:81:7a:b0:bb:0c
type = 0x9000
We can stack higher layer protocols on top of the Ethernet protocol, like so
5 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
Here, we stacked IP, a network layer (Layer 3) protocol on top of Ethernet, a data
link layer (Layer 2) protocol.
ARP Scanner
def arp_scan(ip):
return result
6 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
<received_packet>] .
TCP Scanner
return result
• If dport=[80, 443] , the TCP packet will be sent to both port 80 (HTTP) and
port 443 (HTTPS)
• If dport=(0, 1000) , the TCP packet will be sent to all ports from port 0 to port
1000.
7 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
Hence, the ports parameter of our function can be either an integer, a list or a
tuple.
flags="S" sets the SYN flag in the TCP packet. If the receiving port is open, it
should reply with a packet with flags set to "SA" (for SYN+ACK).
socket.gaierror
socket.gaierror is raised when either the IP address provided is invalid, or a
hostname provided could not be resolved by the DNS service. We catch this
exception and raise a more meaningful exception to the user instead.
because we are dealing with a Layer 3 packet. Both methods return the same
results but srp() is for Layer 2 packets.
Full Code
I used argparse to turn our scanner into a command-line application, and added
some documentation. The full code is embedded below. You can also find it at my
GitHub repository.
8 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
1 import argparse
2 from scapy.all import *
3
4
5 def arp_scan(ip):
6 """
7 Performs a network scan by sending ARP requests to an IP address or a range of IP addresse
8
9 Args:
10 ip (str): An IP address or IP address range to scan. For example:
11 - 192.168.1.1 to scan a single IP address
12 - 192.168.1.1/24 to scan a range of IP addresses.
13
14 Returns:
15 A list of dictionaries mapping IP addresses to MAC addresses. For example:
16 [
17 {'IP': '192.168.2.1', 'MAC': 'c4:93:d9:8b:3e:5a'}
18 ]
19 """
20 request = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip)
21
22 ans, unans = srp(request, timeout=2, retry=1)
23 result = []
24
25 for sent, received in ans:
26 result.append({'IP': received.psrc, 'MAC': received.hwsrc})
27
28 return result
29
30
31 def tcp_scan(ip, ports):
32 """
33 Performs a TCP scan by sending SYN packets to <ports>.
34
35 Args:
36 ip (str): An IP address or hostname to target.
37 ports (list or tuple of int): A list or tuple of ports to scan.
38
39 Returns:
40 A list of ports that are open.
41 """
42 try:
43 syn = IP(dst=ip) / TCP(dport=ports, flags="S")
44 except socket.gaierror:
45 raise ValueError('Hostname {} could not be resolved.'.format(ip))
9 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
46
47 ans, unans = sr(syn, timeout=2, retry=1)
48 result = []
49
50 for sent, received in ans:
51 if received[TCP].flags == "SA":
52 result.append(received[TCP].sport)
53
54 return result
55
56
57 def main():
58 parser = argparse.ArgumentParser()
59 subparsers = parser.add_subparsers(
60 dest="command", help="Command to perform.", required=True
61 )
62
63 arp_subparser = subparsers.add_parser(
64 'ARP', help='Perform a network scan using ARP requests.'
65 )
66 arp_subparser.add_argument(
67 'IP', help='An IP address (e.g. 192.168.1.1) or address range (e.g. 192.168.1.1/24) to
68 )
69
70 tcp_subparser = subparsers.add_parser(
71 'TCP', help='Perform a TCP scan using SYN packets.'
72 )
73 tcp_subparser.add_argument('IP', help='An IP address or hostname to target.')
74 tcp_subparser.add_argument(
75 'ports', nargs='+', type=int,
76 help='Ports to scan, delimited by spaces. When --range is specified, scan a range of p
77 )
78 tcp_subparser.add_argument(
79 '--range', action='store_true',
80 help='Specify a range of ports. When this option is specified, <ports> should be given
81 )
82
83 args = parser.parse_args()
84
85 if args.command == 'ARP':
86 result = arp_scan(args.IP)
87
88 for mapping in result:
89 print('{} ==> {}'.format(mapping['IP'], mapping['MAC']))
90
91 elif args.command == 'TCP':
92 if args range:
10 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...
92 if args.range:
93 ports = tuple(args.ports)
94 else:
95 ports = args.ports
96
97 try:
98 result = tcp_scan(args.IP, ports)
99 except ValueError as error:
100 print(error)
101 exit(1)
102
103 for port in result:
104 print('Port {} is open.'.format(port))
105
Conclusion
That’s all! I hope that you’ve enjoyed reading this as much as I have enjoyed
writing it. If you have any questions, please feel free to let me know in the
comments.
Follow
11 of 19 4/14/24, 15:16