如何用Scapy写一个端口扫描器?
scapy官方教程
常見的端口掃描類型有:
1.?TCP?連接掃描 2.?TCP?SYN?掃描(也稱為半開放掃描或stealth掃描) 3.?TCP?圣誕樹(Xmas?Tree)掃描 4.?TCP?FIN?掃描 5.?TCP?空掃描(Null) 6.?TCP?ACK?掃描 7.?TCP?窗口掃描 8.?UDP?掃描下面先講解每種掃描的原理,隨后提供具體實現代碼。
TCP 連接掃描?
客戶端與服務器建立 TCP 連接要進行一次三次握手,如果進行了一次成功的三次握手,則說明端口開放。
客戶端想要連接服務器80端口時,會先發送一個帶有 SYN 標識和端口號的 TCP 數據包給服務器(本例中為80端口)。如果端口是開放的,則服務器會接受這個連接并返回一個帶有 SYN 和 ACK 標識的數據包給客戶端。隨后客戶端會返回帶有 ACK 和 RST 標識的數據包,此時客戶端與服務器建立了連接。如果完成一次三次握手,那么服務器上對應的端口肯定就是開放的。
當客戶端發送一個帶有 SYN 標識和端口號的 TCP 數據包給服務器后,如果服務器端返回一個帶 RST 標識的數據包,則說明端口處于關閉狀態。
代碼:
#!?/usr/bin/pythonimport?logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from?scapy.all?import?*dst_ip?=?"10.0.0.1" src_port?=?RandShort() dst_port=80tcp_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)print?"Open" elif?(tcp_connect_scan_resp.getlayer(TCP).flags?==?0x14):print?"Closed"</type?'nonetype'>TCP SYN 掃描
這個技術同 TCP 連接掃描非常相似。同樣是客戶端向服務器發送一個帶有 SYN 標識和端口號的數據包,如果目標端口開發,則會返回帶有 SYN 和 ACK 標識的 TCP 數據包。但是,這時客戶端不會返回 RST+ACK 而是返回一個只帶有 RST 標識的數據包。這種技術主要用于躲避防火墻的檢測。
如果目標端口處于關閉狀態,那么同之前一樣,服務器會返回一個 RST 數據包。
代碼:
#!?/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=80stealth_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)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"</type?'nonetype'>TCP 圣誕樹(Xmas Tree)掃描
在圣誕樹掃描中,客戶端會向服務器發送帶有 PSH,FIN,URG 標識和端口號的數據包給服務器。如果目標端口是開放的,那么不會有任何來自服務器的回應。
如果服務器返回了一個帶有 RST 標識的 TCP 數據包,那么說明端口處于關閉狀態。
但如果服務器返回了一個 ICMP 數據包,其中包含 ICMP 目標不可達錯誤類型3以及 ICMP 狀態碼為1,2,3,9,10或13,則說明目標端口被過濾了無法確定是否處于開放狀態。
代碼:
#!?/usr/bin/pythonimport?logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from?scapy.all?import?*dst_ip?=?"10.0.0.1" src_port?=?RandShort() dst_port=80xmas_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"</type?'nonetype'>TCP FIN掃描
FIN 掃描會向服務器發送帶有 FIN 標識和端口號的 TCP 數據包。如果沒有服務器端回應則說明端口開放。
如果服務器返回一個 RST 數據包,則說明目標端口是關閉的。
如果服務器返回了一個 ICMP 數據包,其中包含 ICMP 目標不可達錯誤類型3以及 ICMP 代碼為1,2,3,9,10或13,則說明目標端口被過濾了無法確定端口狀態。
代碼:
#!?/usr/bin/pythonimport?logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from?scapy.all?import?*dst_ip?=?"10.0.0.1" src_port?=?RandShort() dst_port=80fin_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"</type?'nonetype'>TCP 空掃描(Null)
在空掃描中,客戶端發出的 TCP 數據包僅僅只會包含端口號而不會有其他任何的標識信息。如果目標端口是開放的則不會回復任何信息。
如果服務器返回了一個 RST 數據包,則說明目標端口是關閉的。
如果返回 ICMP 錯誤類型3且代碼為1,2,3,9,10或13的數據包,則說明端口被服務器過濾了。
代碼:
#!?/usr/bin/pythonimport?logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from?scapy.all?import?*dst_ip?=?"10.0.0.1" src_port?=?RandShort() dst_port=80null_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"</type?'nonetype'>TCP ACK掃描
ACK 掃描不是用于發現端口開啟或關閉狀態的,而是用于發現服務器上是否存在有狀態防火墻的。它的結果只能說明端口是否被過濾。再次強調,ACK 掃描不能發現端口是否處于開啟或關閉狀態。
客戶端會發送一個帶有 ACK 標識和端口號的數據包給服務器。如果服務器返回一個帶有 RST 標識的 TCP 數據包,則說明端口沒有被過濾,不存在狀態防火墻。
如果目標服務器沒有任何回應或者返回ICMP 錯誤類型3且代碼為1,2,3,9,10或13的數據包,則說明端口被過濾且存在狀態防火墻。
#!?/usr/bin/pythonimport?logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from?scapy.all?import?*dst_ip?=?"10.0.0.1" src_port?=?RandShort() dst_port=80ack_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)"</type?'nonetype'>TCP窗口掃描
TCP 窗口掃描的流程同 ACK 掃描類似,同樣是客戶端向服務器發送一個帶有 ACK 標識和端口號的 TCP 數據包,但是這種掃描能夠用于發現目標服務器端口的狀態。在 ACK 掃描中返回 RST 表明沒有被過濾,但在窗口掃描中,當收到返回的 RST 數據包后,它會檢查窗口大小的值。如果窗口大小的值是個非零值,則說明目標端口是開放的。
如果返回的 RST 數據包中的窗口大小為0,則說明目標端口是關閉的。
代碼:
#!?/usr/bin/pythonimport?logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from?scapy.all?import?*dst_ip?=?"10.0.0.1" src_port?=?RandShort() dst_port=80window_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"</type?'nonetype'>UDP掃描
TCP 是面向連接的協議,而UDP則是無連接的協議。
面向連接的協議會先在客戶端和服務器之間建立通信信道,然后才會開始傳輸數據。如果客戶端和服務器之間沒有建立通信信道,則不會有任何產生任何通信數據。
無連接的協議則不會事先建立客戶端和服務器之間的通信信道,只要客戶端到服務器存在可用信道,就會假設目標是可達的然后向對方發送數據。
客戶端會向服務器發送一個帶有端口號的 UDP 數據包。如果服務器回復了 UDP 數據包,則目標端口是開放的。
如果服務器返回了一個 ICMP 目標不可達的錯誤和代碼3,則意味著目標端口處于關閉狀態。
如果服務器返回一個 ICMP 錯誤類型3且代碼為1,2,3,9,10或13的數據包,則說明目標端口被服務器過濾了。
但如果服務器沒有任何相應客戶端的 UDP 請求,則可以斷定目標端口可能是開放或被過濾的,無法判斷端口的最終狀態。
代碼:
#!?/usr/bin/pythonimport?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=10def?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)</type?'nonetype'></type?'nonetype'>??下面解釋下上述代碼中的一些函數和變量:??
RandShort():產生隨機數? type():獲取數據類型? sport:源端口號 dport:目標端口號 timeout:等待相應的時間 haslayer():查找指定層:TCP或UDP或ICMP getlayer():獲取指定層:TCP或UDP或ICMP以上掃描的概念可以被用于“多端口掃描”,源碼可以參考這里:https://github.com/interference-security/Multiport
Scapy 是一個非常好用的工具,使用它可以非常簡單的構建自己的數據包,還可以很輕易的處理數據包的發送和相應。
(譯者注:上述所有代碼均在Kali 2.0下測試通過,建議讀者在Linux環境下測試代碼,如想在Windows上測試,請參見Scapy官方文檔配置好scapy環境)
*原文地址:infosecinstitute,FB小編xiaix編譯,轉自須注明來自FreeBuf黑客與極客(FreeBuf.COM)
總結
以上是生活随笔為你收集整理的如何用Scapy写一个端口扫描器?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: icmp数据包BE、LE解释
- 下一篇: 暮光之城4下高清下载地址|暮光之城4第二