Port scanning using Scapy
TCP connect scan
TCP connect is a three-way handshake between the client and the server. If the three-way handshake takes place, then communication has been established.
What should you learn next?
A client trying to connect to a server on port 80 initializes the connection by sending a TCP packet with the SYN flag set and the port to which it wants to connect (in this case port 80). If the port is open on the server and is accepting connections, it responds with a TCP packet with the SYN and ACK flags set. The connection is established by the client sending an acknowledgement ACK and RST flag in the final handshake. If this three-way handshake is completed, then the port on the server is open.
The client sends the first handshake using the SYN flag and port to connect to the server in a TCP packet. If the server responds with a RST instead of a SYN-ACK, then that particular port is closed on the server.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
tcp_connect_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="S"),timeout=10)
if(str(type(tcp_connect_scan_resp))=="<type 'NoneType'>"):
print "Closed"
elif(tcp_connect_scan_resp.haslayer(TCP)):
if(tcp_connect_scan_resp.getlayer(TCP).flags == 0x12):
send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="AR"),timeout=10)
print "Open"
elif (tcp_connect_scan_resp.getlayer(TCP).flags == 0x14):
print "Closed"
[/python]
TCP stealth scan
This technique is similar to the TCP connect scan. The client sends a TCP packet with the SYN flag set and the port number to connect to. If the port is open, the server responds with the SYN and ACK flags inside a TCP packet. But this time the client sends a RST flag in a TCP packet and not RST+ACK, which was the case in the TCP connect scan. This technique is used to avoid port scanning detection by firewalls.
The closed port check is same as that of TCP connect scan. The server responds with an RST flag set inside a TCP packet to indicate that the port is closed on the server
The Code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
stealth_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="S"),timeout=10)
if(str(type(stealth_scan_resp))=="<type 'NoneType'>"):
print "Filtered"
elif(stealth_scan_resp.haslayer(TCP)):
if(stealth_scan_resp.getlayer(TCP).flags == 0x12):
send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="R"),timeout=10)
print "Open"
elif (stealth_scan_resp.getlayer(TCP).flags == 0x14):
print "Closed"
elif(stealth_scan_resp.haslayer(ICMP)):
if(int(stealth_scan_resp.getlayer(ICMP).type)==3 and int(stealth_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
print "Filtered"
[/python]
XMAS scan
In the XMAS scan, a TCP packet with the PSH, FIN, and URG flags set, along with the port to connect to, is sent to the server. If the port is open, then there will be no response from the server.
If the server responds with the RST flag set inside a TCP packet, the port is closed on the server.
If the server responds with the ICMP packet with an ICMP unreachable error type 3 and ICMP code 1, 2, 3, 9, 10, or 13, then the port is filtered and it cannot be inferred from the response whether the port is open or closed.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
xmas_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="FPU"),timeout=10)
if (str(type(xmas_scan_resp))=="<type 'NoneType'>"):
print "Open|Filtered"
elif(xmas_scan_resp.haslayer(TCP)):
if(xmas_scan_resp.getlayer(TCP).flags == 0x14):
print "Closed"
elif(xmas_scan_resp.haslayer(ICMP)):
if(int(xmas_scan_resp.getlayer(ICMP).type)==3 and int(xmas_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
print "Filtered"
[/python]
FIN scan
The FIN scan utilizes the FIN flag inside the TCP packet, along with the port number to connect to on the server. If there is no response from the server, then the port is open.
If the server responds with an RST flag set in the TCP packet for the FIN scan request packet, then the port is closed on the server.
An ICMP packet with ICMP type 3 and code 1, 2, 3, 9, 10, or 13 in response to the FIN scan packet from the client means that the port is filtered and the port state cannot be found.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
fin_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="F"),timeout=10)
if (str(type(fin_scan_resp))=="<type 'NoneType'>"):
print "Open|Filtered"
elif(fin_scan_resp.haslayer(TCP)):
if(fin_scan_resp.getlayer(TCP).flags == 0x14):
print "Closed"
elif(fin_scan_resp.haslayer(ICMP)):
if(int(fin_scan_resp.getlayer(ICMP).type)==3 and int(fin_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
print "Filtered"
[/python]
NULL scan
In a NULL scan, no flag is set inside the TCP packet. The TCP packet is sent along with the port number only to the server. If the server sends no response to the NULL scan packet, then that particular port is open.
If the server responds with the RST flag set in a TCP packet, then the port is closed on the server.
An ICMP error of type 3 and code 1, 2, 3, 9, 10, or 13 means the port is filtered on the server.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
null_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags=""),timeout=10)
if (str(type(null_scan_resp))=="<type 'NoneType'>"):
print "Open|Filtered"
elif(null_scan_resp.haslayer(TCP)):
if(null_scan_resp.getlayer(TCP).flags == 0x14):
print "Closed"
elif(null_scan_resp.haslayer(ICMP)):
if(int(null_scan_resp.getlayer(ICMP).type)==3 and int(null_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
print "Filtered"
[/python]
TCP ACK scan
The TCP ACK scan is not used to find the open or closed state of a port; rather, it is used to find if a stateful firewall is present on the server or not. It only tells if the port is filtered or not. This scan type cannot find the open/closed state of the port.
A TCP packet with the ACK flag set and the port number to connect to is sent to the server. If the server responds with the RSP flag set inside a TCP packet, then the port is unfiltered and a stateful firewall is absent.
If the server doesn't respond to our TCK ACK scan packet or if it responds with a TCP packet with ICMP type 3 or code 1, 2, 3, 9, 10, or 13 set, then the port is filtered and a stateful firewall is present.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
ack_flag_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="A"),timeout=10)
if (str(type(ack_flag_scan_resp))=="<type 'NoneType'>"):
print "Stateful firewall presentn(Filtered)"
elif(ack_flag_scan_resp.haslayer(TCP)):
if(ack_flag_scan_resp.getlayer(TCP).flags == 0x4):
print "No firewalln(Unfiltered)"
elif(ack_flag_scan_resp.haslayer(ICMP)):
if(int(ack_flag_scan_resp.getlayer(ICMP).type)==3 and int(ack_flag_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
print "Stateful firewall presentn(Filtered)"
[/python]
TCP window scan
A TCP window scan uses the same technique as that of TCP ACK scan. It also sends a TCP packet with the ACK flag set and the port number to connect to. But this scan type can be used to find the state of the port on the server. In a TCP ACK scan, an RST indicates an unfiltered state. But in a TCP windows scan, when an RST is received from the server, it then checks the value of the windows size. If the value of window size is positive, then the port is open on the server.
If the windows size of the TCP packet with the RST flag set to zero, then the port is closed on the server.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=80
window_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="A"),timeout=10)
if (str(type(window_scan_resp))=="<type 'NoneType'>"):
print "No response"
elif(window_scan_resp.haslayer(TCP)):
if(window_scan_resp.getlayer(TCP).window == 0):
print "Closed"
elif(window_scan_resp.getlayer(TCP).window > 0):
print "Open"
[/python]
UDP scan
TCP is a connection-oriented protocol and UDP is a connection-less protocol.
A connection-oriented protocol is a protocol in which a communication channel should be available between the client and server and only then is a further packet transfer made. If there is no communication channel between the client and the server, then no further communication takes place.
A Connection-less protocol is a protocol in which a packet transfer takes place without checking if there is a communication channel available between the client and the server. The data is just sent on to the destination, assuming that the destination is available.
The client sends a UDP packet with the port number to connect to. If the server responds to the client with a UDP packet, then that particular port is open on the server.
The client sends a UDP packet and the port number it wants to connect to, but the server responds with an ICMP port unreachable error type 3 and code 3, meaning that the port is closed on the server.
If the server responds to the client with an ICMP error type 3 and code 1, 2, 9, 10, or 13, then that port on the server is filtered.
If the server sends no response to the client's UDP request packet for that port, it can be concluded that the port on the server is either open or filtered. No final state of the port can be decided.
The code:
[python]
#! /usr/bin/python
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "10.0.0.1"
src_port = RandShort()
dst_port=53
dst_timeout=10
def udp_scan(dst_ip,dst_port,dst_timeout):
udp_scan_resp = sr1(IP(dst=dst_ip)/UDP(dport=dst_port),timeout=dst_timeout)
if (str(type(udp_scan_resp))=="<type 'NoneType'>"):
retrans = []
for count in range(0,3):
retrans.append(sr1(IP(dst=dst_ip)/UDP(dport=dst_port),timeout=dst_timeout))
for item in retrans:
if (str(type(item))!="<type 'NoneType'>"):
udp_scan(dst_ip,dst_port,dst_timeout)
return "Open|Filtered"
elif (udp_scan_resp.haslayer(UDP)):
return "Open"
elif(udp_scan_resp.haslayer(ICMP)):
if(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code)==3):
return "Closed"
elif(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code) in [1,2,9,10,13]):
return "Filtered"
udp_scan(dst_ip,dst_port,dst_timeout)
[/python]
Explanation of some functions and variables used in the above codes:
This concept of scanning has been used in the "multiport scanner available at:
https://github.com/interference-security/Multiport
You can go ahead and see the same implementation of port scanning in this project.
Scapy is a very easy to use tool and makes it really simple to create your own packets and understand what request packet is being sent and what response packet is being received.
Become a Certified Ethical Hacker, guaranteed!
Get training from anywhere to earn your Certified Ethical Hacker (CEH) Certification — backed with an Exam Pass Guarantee.
Sources