UDP 端口掃描
要對端口進行掃描需要先了解一下UDP協議的特徵
UDP是無連接通信協議,即在數據傳輸時,數據的發送端和接收端不建立邏輯連接。簡單來說,當一臺計算機向另外一臺計算機發送數據時, 發送端不會確認接收端是否存在,就會發出數據,同樣接收端在收到數據時,也不會向發送端反饋是否收到數據。
由於使用UDP協議消耗資源小,通信效率高,所以通常都會用於音頻、視頻和普通數據的傳輸例如視頻會議都使用UDP協議, 因爲這種情況即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。
但是在使用UDP協議傳送數據時,由於UDP的面向無連接性,不能保證數據的完整性, 因此在傳輸重要數據時不建議使用UDP協議。UDP傳輸數據被限制在64K以內。
問題記錄
在使用scapy構建UDP數據包,調用多線程去發包沒收到響應
解釋器版本爲python3.8.2
-
使用構造的UDP數據包去訪問未開啓UDP服務端口時,會返回ICMP端口不可達
-
靶機50-60端口範圍只開放了53domain端口,理論來說在給這個區間的範圍內發送UDP端口請求包,應該會收到9個ICMP端口不可達,然而實際只收到了6個響應,導致程序功能沒有實現
-
初步懷疑是靶機防火牆可能存在過濾,打開靶機iptables -L 防火牆並沒有出入站規則,但死活就是沒有收到相應數量的ICMP端口不可達。
-
嘗試添加防火牆出站策略,放行所有UDP,ICMP出站數據包,試試是不是防火牆的問題,再打開tcpdumo抓包
-
發現和防火牆一毛錢關係都沒有,靶機只響應了6個ICMP端口不可達,剩下三個不知道爲啥沒響應,搜了一下發現一篇文章指出了問題,大致意思是缺少了某個函數。
-
看了解決問題的程序,奈何自己太垃圾是用其他語言寫的,看不懂。開始懷疑自己的是不是對UDP協議理解有問題了,回翻看筆記找到了沒收到ICMP不可達的原因,第一和第三都排除了,就剩第二了
上網搜了一下ICMP端口不可達響應速率,發現了一篇文章寫了華爲設備對icmp信息的relay速度有限制,目的就是爲了防止被dos佔用設備性能,造成正常服務無法工作。然後再翻了一下自己路由器看到了一個熟悉的菊花logo。
莫名有了種想砸路由器的衝動。迴歸正題,找到問題所在了就可以通過修改代碼來解決問題了。
四層主機端口發現
四層發現指利用OSI中的傳輸層協議進行主機發現,一般使用TCP、UDP探測。
優點: 1、可以探測遠程主機; 2、比三層發現更爲可靠
缺點:花費時間更長,可能被防火牆過濾
探測目標主機是否開放
向目標主機的相應端口發送UDP數據包,如果沒有返回回覆,則證明這個端口是開放
需要注意的是這點是利用了ICMP不可達的原理,如果目標主機不在線那麼也沒有返回消息,導致誤判,所以要求目標主機必須在線
數據包構造
result = sr1(IP(dst="192.168.3.107")/UDP(dport=53),timeout=1,verbose=0)
最常用的udp端口爲53DNS服務
代碼部分
掃描模塊
功能
接收主函數傳過來的IP,端口構造UDP數據包
向端口批量發送數據包,並利用是否有返回消息判斷端口是否在線
def scan(ip, port):
try:
packet = IP(dst=ip)/UDP(dport=port,sport=randint(1,65535))
result = sr1(packet,timeout=5,verbose=0) # timeout=5 爲數據包提供五秒等待時間,如果沒有回覆就放棄,verbose=0,不顯示輸出
time.sleep(0.1)
if result is None:
print(port)
except:
print("send error")
主函數
- 功能
接收用戶輸入的參數
判斷用戶輸入的端口是單個還是端口範圍
如果是端口範圍,將起始端口和結束端口for循環進行遍歷生成需要掃描的端口
def main():
parser = OptionParser("Usage: targetIP start_Port end_port ") # 輸出幫助信息
parser.add_option("-i", '--host',type="string", dest="tgtIP",help="specify target host website") # 獲取ip地址
parser.add_option("-p", '--port',type="string", dest="hostport",help="specify target host singleport") # 獲取單個端口
parser.add_option("-s", '--start',type="string", dest="startport",help="specify target host start_port") # 獲取結束端口
parser.add_option("-e", '--end',type="string", dest="endport",help="specify target host end port") # 獲取起始端口
options,args= parser.parse_args() #實例化用戶輸入的參數
# 爲用戶輸入的參數建立對象
ip = options.tgtIP
singleport = int(options.hostport)
start = options.startport
end = options.endport
# 如果是單端口要執行的操作
if singleport:
port = singleport
scan(ip,port)
# 如果是端口區間則多線程掃描
if start and end:
start = int(start)
end = int(end)
for p in range(start,end):
port = int(p)
t = Thread(target=scan,args=(ip,port))
t.start()
time.sleep(1)
完整代碼
from random import randint
from time import sleep
from optparse import OptionParser
from scapy.all import *
def scan(ip, port):
try:
packet = IP(dst=ip)/UDP(dport=port,sport=randint(1,65535)) # 隨機src端口,讓掃面不易被察覺
result = sr1(packet,timeout=5,verbose=0) # timeout=5 爲數據包提供五秒等待時間,如果沒有回覆就放棄,verbose=0,不顯示輸出
time.sleep(0.1)
if result is None:
print(ip, port,'is open')
except:
print("send error")
def main():
parser = OptionParser("Usage: targetIP start_Port end_port ") #輸出幫助信息
parser.add_option("-i", '--host',type="string", dest="tgtIP",help="specify target host website") # 獲取ip地址
parser.add_option("-p", '--port',type="string", dest="hostport",help="specify target host singleport") # 獲取單個端口
parser.add_option("-s", '--start',type="string", dest="startport",help="specify target host start_port") # 獲取起始端口
parser.add_option("-e", '--end',type="string", dest="endport",help="specify target host end port") #獲取結束端口
options,args = parser.parse_args() # 實例化用戶輸入的參數
ip = options.tgtIP
singleport = options.hostport
start = options.startport
end = options.endport
# 如果是單端口則執行的操作
if singleport:
port = int(singleport)
scan(ip,port)
# 如果是端口區間則調用多線程去掃描
if start and end:
start = int(start)
end = int(end)
for p in range(start,end):
port = int(p)
t = Thread(target=scan,args=(ip,port))
t.start()
time.sleep(1)
if __name__ == "__main__":
main()
if __name__ == '__main__':
main()
運行效果