National Day holiday is also idle, let's review the port scanning technology. This paper first gives the specific principle of each kind of scanning, and then gives the specific code using scapy.
0x01 basic knowledge
TCP header structure
Because most port scanning technologies are based on TCP, let's review the TCP protocol briefly. The following are from Wikipedia:
- Source connection port (16 bit long) - identify the send connection port
- Destination connection port (16 bit long) - identify the receiving connection port
- If the sequence number (SEQ, 32-bit long) contains the synchronization flag (SYN), this is the original sequence number; the sequence code of the first data bit is the sequence number plus one. If there is no sync flag (SYN), this is the sequence code of the first data bit.
- If the synchronization flag (SYN) is included, this is the original serial number; the serial code of the first data bit is the serial number plus one.
- If there is no sync flag (SYN), this is the sequence code of the first data bit.
- Acknowledgment number (ACK, 32-bit long) - the start sequence number of the data that is expected to be received. That is, the byte length of the received data plus 1.
- Header length (4-bit length) - the offset of the data segment start address calculated in 4 bytes.
- Reserved - set to 0
- Flag urg - 1 indicates a high priority packet, and the emergency pointer field is valid. ACK - 1 indicates the effective PSH of the confirmation number field - 1 indicates the data with push flag, indicating that the receiver should deliver this message segment to the application layer as soon as possible without waiting for the buffer to be full. Rst - a value of 1 indicates a serious error. You may need to recreate a TCP connection. It can also be used to reject illegal message segments and connection requests. Syn - a value of 1 indicates whether this is a connection request or a connection acceptance request, which is used to create a connection and synchronize the sequence number. Fin - a value of 1 indicates that the sender has no data to transmit and requires the connection to be released.
- Urg - 1 indicates a high priority packet, and the emergency pointer field is valid.
- ACK - 1 means the confirmation number field is valid
- PSH - 1 indicates the data with push flag, indicating that the receiver should deliver the message segment to the application layer as soon as possible without waiting for the buffer to be full.
- Rst - a value of 1 indicates a serious error. You may need to recreate a TCP connection. It can also be used to reject illegal message segments and connection requests.
- Syn - a value of 1 indicates whether this is a connection request or a connection acceptance request, which is used to create a connection and synchronize the sequence number.
- Fin - a value of 1 indicates that the sender has no data to transmit and requires the connection to be released.
- Window (win, 16 bit long) - indicates the number of bytes that can be received by the source side of this message from the confirmation number, that is, the size of the receiving window of the source side. For flow control.
- Checksum (16 bit long) - the calculation of the whole TCP message segment, including the TCP header and TCP data, in 16 bit words. This is a mandatory field.
- Emergency pointer (16 bit long) - the sequence number of the last byte of emergency data in this report.
- Option field - up to 40 bytes. The start of each option is a 1-byte kind field that describes the type of option. 0: option table end (1 byte) 1: no action (1 byte) for word boundary alignment between option fields. 2: The maximum segment length (4 bytes, maximum segment size, MSS) usually indicates this option in the packets that set the syn flag when creating a connection, indicating the maximum length of the segment that can be received by the local end. Generally, MSS is set to (mtu-40) bytes, and the length of IP datagram carrying TCP message segment will not exceed MTU, so as to avoid IP fragmentation of the machine. It can only appear in the synchronous message segment, otherwise it will be ignored. 3: Window expansion factor (4 bytes, wscale), value 0-14. The number of bits used to shift the value of the TCP window to the left. It can only appear in the synchronous message segment, otherwise it will be ignored. This is because the current TCP receive data buffer (receive window) is usually longer than 65535 bytes. 4: Sackok - the sender supports and agrees to use the sack option. 5: Options for sack to actually work. 8: Timestamp (10 bytes, TCP timestamps option, tsopt) timestamp value field, tsval (4 bytes) timestamp echo reply field, tsecr (4 bytes)
- 0: end of option table (1 byte)
- 1: No action (1 byte) is used for word boundary alignment between option fields.
- 2: The maximum segment length (4 bytes, maximum segment size, MSS) usually indicates this option in the packets that set the syn flag when creating a connection, indicating the maximum length of the segment that can be received by the local end. Generally, MSS is set to (mtu-40) bytes, and the length of IP datagram carrying TCP message segment will not exceed MTU, so as to avoid IP fragmentation of the machine. It can only appear in the synchronous message segment, otherwise it will be ignored.
- 3: Window expansion factor (4 bytes, wscale), value 0-14. The number of bits used to shift the value of the TCP window to the left. It can only appear in the synchronous message segment, otherwise it will be ignored. This is because the current TCP receive data buffer (receive window) is usually longer than 65535 bytes.
- 4: Sackok - the sender supports and agrees to use the sack option.
- 5: Options for sack to actually work.
- 8: Timestamp (10 bytes, TCP timestamps option, tsopt) timestamp value field, tsval (4 bytes) timestamp echo reply field, tsecr (4 bytes)
- Timestamp value field of sender (tsval, 4 bytes)
- Timestamp Echo Reply field (TSecr, 4 bytes)
ICMP message type
Why should we talk about ICMP message types here? This is mainly to understand the ICMP response returned after the firewall filters and scans data packets. In fact, there is nothing to talk about. Just finish with the following picture:
Generally, if the packet is filtered by the firewall, an ICMP packet will be received, which is of type 3 and code 1, 2, 3, 9, 10 or 13.
Scapy Foundation
This article focuses on how to construct packets and how to send packets.
Construct packet
The packet creation of scapy is based on our four layer reference model of TCP / IP, including link layer, network layer, transport layer and application layer. Scapy has written classes for each layer. What we need to do is instantiate these classes. Some operations on the packet are to call the class's methods or change the class's parameter values. Each layer has its own creation functions, such as ip(), tcp(), udp(), and so on. Different layers are connected by "/".
Message template
A large number of message templates are built in scapy, which can be viewed through LS under the Python interpreter
>>> ls()
ARP : ARP
ASN1_Packet : None
BOOTP : BOOTP
CookedLinux : cooked linux
DHCP : DHCP options
DHCP6 : DHCPv6 Generic Message)
DHCP6OptAuth : DHCP6 Option - Authentication
DHCP6OptBCMCSDomains : DHCP6 Option - BCMCS Domain Name List
DHCP6OptBCMCSServers : DHCP6 Option - BCMCS Addresses List
DHCP6OptClientFQDN : DHCP6 Option - Client FQDN
DHCP6OptClientId : DHCP6 Client Identifier Option
DHCP6OptDNSDomains : DHCP6 Option - Domain Search List option
DHCP6OptDNSServers : DHCP6 Option - DNS Recursive Name Server
DHCP6OptElapsedTime : DHCP6 Elapsed Time Option
DHCP6OptGeoConf :
DHCP6OptIAAddress : DHCP6 IA Address Option (IA_TA or IA_NA suboption)
DHCP6OptIAPrefix : DHCP6 Option - IA_PD Prefix option
DHCP6OptIA_NA : DHCP6 Identity Association for Non-temporary Addresses Option
DHCP6OptIA_PD : DHCP6 Option - Identity Association for Prefix Delegation
DHCP6OptIA_TA : DHCP6 Identity Association for Temporary Addresses Option
DHCP6OptIfaceId : DHCP6 Interface-Id Option
DHCP6OptInfoRefreshTime : DHCP6 Option - Information Refresh Time
...
The commonly used templates include: ether, IP, TCP, UDP and other message templates. Here we focus on the following TCP templates:
>>> ls(TCP)
sport : ShortEnumField = (20)
dport : ShortEnumField = (80)
seq : IntField = (0)
ack : IntField = (0)
dataofs : BitField (4 bits) = (None)
reserved : BitField (4 bits) = (0)
flags : FlagsField (8 bits) = (2)
window : ShortField = (8192)
chksum : XShortField = (None)
urgptr : ShortField = (0)
options : TCPOptionsField = ({})
Now let's build a data package by ourselves:
>>> p=sr1(p1,timeout=10)
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
>>> p1=IP(src="192.168.199.100",dst="192.168.199.1")/TCP(sport=RandShort(),dport=80,flags='S')
>>> p1.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= tcp
chksum= None
src= 192.168.199.100
dst= 192.168.199.1
\options\
###[ TCP ]###
sport= <RandShort>
dport= www
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= {}
For the TCP packet constructed above, we use the random function to randomly select the port when sending the packet. The destination port is 80. Flags = s means that we will identify syn position 1 in the bit, that is to say, we construct a syn packet.
flags=S
SYN
Send and receive packets
The SR () function is used to send packets and receive replies. This function returns a pair of packets and their replies, as well as packets with or without replies. The sr1() function is a variant that returns an answer packet. Now let's try to send the data packet constructed above directly:
>>> p=sr1(p1,timeout=10)
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 44
id= 0
flags= DF
frag= 0L
ttl= 64
proto= tcp
chksum= 0x2b15
src= 192.168.199.1
dst= 192.168.199.100
\options\
###[ TCP ]###
sport= www
dport= 64694
seq= 3511850663
ack= 1
dataofs= 6L
reserved= 0L
flags= SA
window= 14600
chksum= 0xea54
urgptr= 0
options= [('MSS', 1460)]
###[ Padding ]###
load= '\x00\x00'
>>> hexdump(p)
0000 45 00 00 2C 00 00 40 00 40 06 2B 15 C0 A8 C7 01 E..,[email protected]@.+.....
0010 C0 A8 C7 64 00 50 FC B6 D1 52 96 A7 00 00 00 01 ...d.P...R......
0020 60 12 39 08 EA 54 00 00 02 04 05 B4 00 00 `.9..T........
Note that the middle identification bit of the reply packet we received above is SA, which means that we have received the syn + ACK packet, and the corresponding hex below is 0x12.
SA
SYN+ACK
0x12
0x02 port scan
Port scan type
Common port scan types are:
- TCP connection scan
- TCP syn scan (also known as semi open scan or stearth scan)
- TCP Xmas tree scan
- TCP fin scan
- TCP empty scan (null)
- TCP ack scan
- TCP window scan
Because there are too many illustrations, I don't want to steal them directly. The original drawings are all from infosec Institute.
TCP connection scan
The principle is very simple, that is to use the client and the server to establish a TCP connection, we must have three handshakes. If we can successfully complete one TCP three handshake, then the target port is open.
Normally, when the port is open, the server will return a syn + ack after receiving the syn as shown in the figure above. At this time, we only need to return an ACK + RST to complete the three handshakes and reset the connection. If the target port is closed, the server will return to RST to reset the connection as shown in the figure below.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port=80
tcp_connect_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='S'),timeout=10)
# 判断是否收到应答包
if type(tcp_connect_scan) == type(None):
print "[-] Port is closed."
# 判断收到的应答包是否具有TCP层
elif tcp_connect_scan.haslayer(TCP):
# 判断是否为SYN+ACK数据包
if tcp_connect_scan.getlayer(TCP).flags == 0x12:
send(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='AR'))
print "[+] Port is open."
# 判断是否为RST数据包
elif tcp_connect_scan.getlayer(TCP).flags == 0x14:
print "[-] Port is closed."
TCP syn scan
The principle of TCP syn scanning is basically the same as the previous TCP connection scanning. The difference lies in that if the target port is open, only RST packets are returned instead of ACK + rst. Then you may have to ask why you do this, which is to avoid the detection of firewall.
Similarly, if the target returns an RST packet directly, it indicates that the port is down.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port=80
tcp_syn_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='S'),timeout=10)
# 判断是否收到应答包
if type(tcp_syn_scan) == type(None):
print "[-] Port is closed."
# 判断收到的应答包是否具有TCP层
elif tcp_syn_scan.haslayer(TCP):
# 判断是否为SYN+ACK数据包
if tcp_syn_scan.getlayer(TCP).flags == 0x12:
send(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='R'))
print "[+] Port is open."
# 判断是否为RST数据包
elif tcp_syn_scan.getlayer(TCP).flags == 0x14:
print "[-] Port is closed."
# 判断数据包是否具有ICMP层
elif tcp_syn_scan.haslayer(ICMP):
# 判断是否被防火墙过滤
if tcp_syn_scan.getlayer(TCP).type == 3 and tcp_syn_scan.getlayer(TCP).code in [1,2,3,9,10,13]:
print "[-] Filtered"
TCP Xmas tree scan
The principle of Christmas tree scanning is that the client sends packets with PSH, fin, urg identification and port number to the server. If the port is open, the server will not respond.
If the server returns an RST packet, it indicates that the port is closed.
Similarly, if the returned ICMP packet is received, it indicates that the packet is filtered.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port=80
tcp_xmas_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='PFU'),timeout=10)
# 判断是否收到应答包
if type(tcp_xmas_scan) == type(None):
print "[+] Port is open|filtered."
# 判断收到的应答包是否具有TCP层
elif tcp_xmas_scan.haslayer(TCP):
# 判断是否为RST数据包
if tcp_xmas_scan.getlayer(TCP).flags == 0x14:
print "[-] Port is closed."
# 判断数据包是否具有ICMP层
elif tcp_xmas_scan.haslayer(ICMP):
# 判断是否被防火墙过滤
if tcp_xmas_scan.getlayer(TCP).type == 3 and tcp_xmas_scan.getlayer(TCP).code in [1,2,3,9,10,13]:
print "[-] Filtered."
TCP fin scan
TCP fin scanning is similar to Christmas tree scanning, except that packets with fin identification and port number are sent to the server. Similarly, if the server does not respond, the port is in an open state (whether it is filtered by the firewall or not).
If the server returns an RST packet, the port is closed.
Similarly, if the returned ICMP packet is received, it indicates that the packet is filtered.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port=80
tcp_fin_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='F'),timeout=10)
# 判断是否收到应答包
if type(tcp_fin_scan) == type(None):
print "[+] Port is open|filtered."
# 判断收到的应答包是否具有TCP层
elif tcp_fin_scan.haslayer(TCP):
# 判断是否为RST数据包
if tcp_fin_scan.getlayer(TCP).flags == 0x14:
print "[-] Port is closed."
# 判断数据包是否具有ICMP层
elif tcp_xmas_scan.haslayer(ICMP):
# 判断是否被防火墙过滤
if tcp_fin_scan.getlayer(TCP).type == 3 and tcp_fin_scan.getlayer(TCP).code in [1,2,3,9,10,13]:
print "[-] Filtered."
TCP empty scan (null)
TCP null scan is also similar to the previous Christmas tree scan and fin scan. It sends a packet to the server. If the server does not respond, it indicates the port method, but the packet sent by empty scan does not set any identification bit.
If the server returns an RST packet, the port is closed.
Similarly, if the returned ICMP packet is received, it indicates that the packet is filtered.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port=80
tcp_null_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags=''),timeout=10)
# 判断是否收到应答包
if type(tcp_null_scan) == type(None):
print "[+] Port is open|filtered."
# 判断收到的应答包是否具有TCP层
elif tcp_null_scan.haslayer(TCP):
# 判断是否为RST数据包
if tcp_null_scan.getlayer(TCP).flags == 0x14:
print "[-] Port is closed."
# 判断数据包是否具有ICMP层
elif tcp_null_scan.haslayer(ICMP):
# 判断是否被防火墙过滤
if tcp_null_scan.getlayer(TCP).type == 3 and tcp_null_scan.getlayer(TCP).code in [1,2,3,9,10,13]:
print "[-] Filtered."
TCP ack scan
Using TCP ack scan can not determine whether the port is closed or open, because when sending a TCP message with ack representation to the other party, it will return a message with RST flag, no matter whether the port is open or closed. Therefore, TCP ack scanning cannot be used to determine whether the port is open or closed. But you can use it to scan the firewall configuration, use it to discover firewall rules, determine whether they are stateful or stateless, and which ports are filtered.
Send a packet with ack identification to the server. If the response with RST identification is received, it means that the server has no filtering and there is no state firewall.
If the server returns an ICMP error packet, the target has a state firewall and our packet is filtered.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port=80
tcp_ack_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='A'),timeout=10)
# 判断是否收到应答包
if type(tcp_ack_scan) == type(None):
print "[-] Filtered."
# 判断收到的应答包是否具有TCP层
elif tcp_ack_scan.haslayer(TCP):
# 判断是否为RST数据包
if tcp_ack_scan.getlayer(TCP).flags == 0x14:
print "[+] Unfiltered."
# 判断数据包是否具有ICMP层
elif tcp_ack_scan.haslayer(ICMP):
# 判断是否被防火墙过滤
if tcp_ack_scan.getlayer(TCP).type == 3 and tcp_ack_scan.getlayer(TCP).code in [1,2,3,9,10,13]:
print "[-] Filtered."
TCP window scan
The process of TCP window scanning is similar to ack scanning, which is to send packets with ack identification to the server. The difference is that TCP window scanning will check the size of the window in the received RST packets. If the size of the window in the rst packets is not zero, the target port is open.
If the window size in the rst packet is zero, the target port is closed.
Code:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
from scapy.all import *
dst_ip = "192.168.199.1"
src_port = RandShort()
dst_port = 80
tcp_window_scan = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='A'),timeout=10)
# 判断是否收到应答包
if type(tcp_window_scan) == type(None):
print "[-] Filtered."
# 判断收到的应答包是否具有TCP层
elif tcp_window_scan.haslayer(TCP):
# 判断窗口大小是否为0
if tcp_window_scan.getlayer(TCP).window == 0:
print "[-] Port is closed."
elif tcp_window_scan.getlayer(TCP).window > 0:
print "[+] Port is open."