端口掃描器
參考:https://www.imooc.com/article/286803
常見的端口掃描類型
1. TCP 連接掃描
2. TCP SYN 掃描(也稱為半開放掃描或stealth掃描)
3. TCP 聖誕樹(Xmas Tree)掃描
4. TCP FIN 掃描
5. TCP 空掃描(Null)
6. TCP ACK 掃描
7. TCP 窗口掃描
8. UDP 掃描
1、TCP連接掃描
若客戶端想要連接服務器80端口時,會先發送一個帶有 SYN 標識和端口號的 TCP 數據包給服務器(本例中為80端口)。如果端口是開放的,則服務器會接受這個連接並返回一個帶有 SYN 和 ACK 標識的數據包給客戶端。隨后客戶端會返回帶有 ACK 和 RST 標識的數據包,此時客戶端與服務器建立了連接。
-
如果完成一次三次握手,那么服務器上對應的端口肯定就是開放的。
-
當客戶端發送一個帶有 SYN 標識和端口號的 TCP 數據包給服務器后,如果服務器端返回一個帶 RST 標識的數據包,則說明端口處於關閉狀態
nmap的-sT模式
#! /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))==""):
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)# 全連接 AR => ACK+RST
print( "Open")
elif (tcp_connect_scan_resp.getlayer(TCP).flags == 0x14):
print( "Closed")
2、TCP SYN 掃描
客戶端向服務器發送一個帶有 SYN 標識和端口號的數據包,這種技術主要用於躲避防火牆的檢測。
-
如果目標端口開發,則會返回帶有 SYN 和 ACK 標識的 TCP 數據包。但是,這時客戶端不會返回 RST+ACK 而是返回一個只帶有 RST 標識的數據包。
-
如果目標端口處於關閉狀態,那么同之前一樣,服務器會返回一個 RST 數據包
nmap的-sS模式
#! /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))==""):
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) # 連接 R==>RST
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")
3、TCP 聖誕樹(Xmas Tree)掃描
在聖誕樹掃描中,客戶端會向服務器發送帶有 PSH,FIN,URG 標識和端口號的數據包給服務器。
-
如果目標端口是開放的,那么不會有任何來自服務器的回應。
-
如果服務器返回了一個帶有 RST 標識的 TCP 數據包,那么說明端口處於關閉狀態。
-
如果服務器返回了一個 ICMP 數據包,其中包含 ICMP 目標不可達錯誤類型3以及 ICMP 狀態碼為1,2,3,9,10或13,則說明目標端口被過濾了無法確定是否處於開放狀態。
nmap -sX模式
#! /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))==""):
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")
4、FIN掃描
FIN 掃描會向服務器發送帶有 FIN 標識和端口號的 TCP 數據包。
-
如果沒有服務器端回應則說明端口開放。
-
如果服務器返回一個 RST 數據包,則說明目標端口是關閉的。
-
如果服務器返回了一個 ICMP 數據包,其中包含 ICMP 目標不可達錯誤類型3以及 ICMP 代碼為1,2,3,9,10或13,則說明目標端口被過濾了無法確定端口狀態。
nmap -sF模式
#! /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))==""):
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")
5、TCP 空掃描(Null)
在空掃描中,客戶端發出的 TCP 數據包僅僅只會包含端口號而不會有其他任何的標識信息。
-
如果目標端口是開放的則不會回復任何信息。
-
如果服務器返回了一個 RST 數據包,則說明目標端口是關閉的。
-
如果返回 ICMP 錯誤類型3且代碼為1,2,3,9,10或13的數據包,則說明端口被服務器過濾了。
nmap -sN模式
#! /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))==""):
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")
6、TCP ACK掃描
ACK 掃描不是用於發現端口開啟或關閉狀態的,而是用於發現服務器上是否存在有狀態防火牆的。它的結果只能說明端口是否被過濾。再次強調,ACK 掃描不能發現端口是否處於開啟或關閉狀態。
-
客戶端會發送一個帶有 ACK 標識和端口號的數據包給服務器。如果服務器返回一個帶有 RST 標識的 TCP 數據包,則說明端口沒有被過濾,不存在狀態防火牆。
-
如果目標服務器沒有任何回應或者返回ICMP 錯誤類型3且代碼為1,2,3,9,10或13的數據包,則說明端口被過濾且存在狀態防火牆。
nmap -sA模式
#! /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))==""):
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)")
7、TCP窗口掃描
TCP 窗口掃描的流程同 ACK 掃描類似,同樣是客戶端向服務器發送一個帶有 ACK 標識和端口號的 TCP 數據包,但是這種掃描能夠用於發現目標服務器端口的狀態。在 ACK 掃描中返回 RST 表明沒有被過濾,但在窗口掃描中,當收到返回的 RST 數據包后,它會檢查窗口大小的值。
-
如果窗口大小的值是個非零值,則說明目標端口是開放的。
-
如果返回的 RST 數據包中的窗口大小為0,則說明目標端口是關閉的。
nmap -sW模式
#! /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))==""):
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")
8、UDP掃描
TCP 是面向連接的協議,而UDP則是無連接的協議。
面向連接的協議會先在客戶端和服務器之間建立通信信道,然后才會開始傳輸數據。如果客戶端和服務器之間沒有建立通信信道,則不會有任何產生任何通信數據。
無連接的協議則不會事先建立客戶端和服務器之間的通信信道,只要客戶端到服務器存在可用信道,就會假設目標是可達的然后向對方發送數據。
-
客戶端會向服務器發送一個帶有端口號的 UDP 數據包。如果服務器回復了 UDP 數據包,則目標端口是開放的。
-
如果服務器返回了一個 ICMP 目標不可達的錯誤和代碼3,則意味着目標端口處於關閉狀態。
-
如果服務器返回一個 ICMP 錯誤類型3且代碼為1,2,3,9,10或13的數據包,則說明目標端口被服務器過濾了。
-
如果服務器沒有任何相應客戶端的 UDP 請求,則可以斷定目標端口可能是開放或被過濾的,無法判斷端口的最終狀態。
#! /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))==""):
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))!=""):
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")
print udp_scan(dst_ip,dst_port,dst_timeout)