Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
37 views

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

Uploaded by

giles.harper
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views

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

Uploaded by

giles.harper
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...

Open in app Sign up Sign in

Search

Network Scanning with Scapy in Python


ARP scans and TCP scans with Scapy

Zhang Zeyu · Follow


Published in Python in Plain English
6 min read · Aug 2, 2020

Listen Share

Photo by Kaur Kristjan on Unsplash

What is Network Scanning?


Network Scanning is the process of gathering information about devices in a
computer network, through employing network protocols. It is especially relevant
for network administrators, penetration testers and, well… hackers.

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!

All code for this tutorial can be found at my GitHub repository.

Protocols, Protocols, Protocols


Communications over networks use what we call a protocol stack — building
higher-level, more sophisticated conversations on top of simpler, more
rudimentary conversations. Network layers can be represented by the OSI model
and the TCP/IP model. Each network layer corresponds to a group of layer-
specific network protocols.

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-...

protocol and the TCP protocol.

Address Resolution Protocol (ARP)


ARP maps IP addresses to MAC addresses. IP addresses are logical addresses,
while MAC addresses are physical addresses. When computers communicate with
each other over the network, they will specify a target IP address. However,
switches (which act as packet forwarders) don’t understand IP addresses — they
can only make forwarding decisions based on MAC addresses. Hence, computers
need to determine the MAC address of their intended recipient before sending
out a packet.

If a computer wishes to send a packet to 192.168.52.2, it will first send an ARP


request, asking all devices in the network “who is IP address 192.168.52.2?” The
computer with IP address 192.168.52.2 will respond with “Hi, I am 192.168.52.2.
My MAC address is 03-CA-4B-2C-13–8A.”

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.

Transmission Control Protocol (TCP)


TCP is a transport layer protocol that most services run on. It is a connection-
oriented protocol, meaning that two devices will need to set up a TCP connection
before exchanging data. This is achieved using a 3-way handshake.

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-...

need to install Scapy with pip.

$ pip install scapy

Now, we can start trying out the basic features of Scapy.

$ python3
...
>>> from scapy.all import *

We can create a packet like so:

>>> 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.

We can specify parameters, such as the destination MAC address:

>>> 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

Here, we specified dst='ff:ff:ff:ff:ff:ff' , which is the broadcast address. The


packet is addressed to all devices within the local network.

We can stack higher layer protocols on top of the Ethernet protocol, like so

>>> p = Ether() / IP()


>>> p.show()

5 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...

###[ Ethernet ]###


dst = ff:ff:ff:ff:ff:ff
src = 00:00:00:00:00:00
type = IPv4
###[ 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 \

Here, we stacked IP, a network layer (Layer 3) protocol on top of Ethernet, a data
link layer (Layer 2) protocol.

ARP Scanner

from scapy.all import *

def arp_scan(ip):

request = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip)

ans, unans = srp(request, timeout=2, retry=1)


result = []

for sent, received in ans:


result.append({'IP': received.psrc, 'MAC':
received.hwsrc})

return result

Creating the ARP Request


Wow, what did we do here? First, we stacked ARP on top of Ethernet, and set the
Ethernet destination address to the broadcast address so that all devices on the
local network receive this ARP request.

Sending and Receiving


Next, we called srp() to send the ARP request, and wait for a response. We
specified the timeout to be 2 seconds, and if we do not receive an ARP reply

6 of 19 4/14/24, 15:16
Network Scanning with Scapy in Python | by Zhang Ze... https://python.plainenglish.io/network-scanning-with-...

within 2 seconds, we retry 1 time before giving up.

Analysing the Results


srp() returns two lists: a list of answered requests and a list of unanswered
requests. Within the list of answered requests are nested lists: [<sent_packet>,

<received_packet>] .

Information contained within the packets is stored as attributes. We use


received.psrc to get the source IP address of the reply and received.hwsrc to get
the source MAC address of the reply.

TCP Scanner

from scapy.all import *

def tcp_scan(ip, ports):


try:
syn = IP(dst=ip) / TCP(dport=ports, flags="S")
except socket.gaierror:
raise ValueError('Hostname {} could not be
resolved.'.format(ip))

ans, unans = sr(syn, timeout=2, retry=1)


result = []

for sent, received in ans:


if received[TCP].flags == "SA":
result.append(received[TCP].sport)

return result

Creating the SYN Packet


Here, we create an IP packet and specify the destination IP address, then stack
TCP on top of it, specifying the destination ports and setting the SYN flag.

Note that dport can be either a single port or a range of ports.

• If dport=80 , the TCP packet will only be sent to port 80 (HTTP).

• 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.

Sending and Receiving


Again, we send the packet and wait for a response. We use sr() instead of srp()

because we are dealing with a Layer 3 packet. Both methods return the same
results but srp() is for Layer 2 packets.

Analysing the Results


Not all replies are SYN+ACK packets! If a port is not open, the target device may
respond with an RST (reset) packet to inform our OS that it does not want to
establish a TCP connection.

We use if received[TCP].flags == “SA” to check the TCP flags of the received


packet. Note that we can use <packet>[<protocol>] to access the protocol-specific
information of the packet. Again, information is stored as attributes and we use
received[TCP].flags to obtain the flags of the received packet.
received[TCP].sport is the source port of the received packet.

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.

Programming Python Cybersecurity Coding Tech

Follow

Written by Zhang Zeyu


100 Followers · Writer for Python in Plain English

Simple is better than complex. Complex is better than complicated.

More from Zhang Zeyu and Python in Plain English

11 of 19 4/14/24, 15:16

You might also like