掃描器篇(六)之python+scapy組合編寫基於UDP協議的端口掃描器,順帶解決UDP協議多線程無響應問題

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個響應,導致程序功能沒有實現
    

n

  • 初步懷疑是靶機防火牆可能存在過濾,打開靶機iptables -L 防火牆並沒有出入站規則,但死活就是沒有收到相應數量的ICMP端口不可達。
    

在這裏插入圖片描述

  • 嘗試添加防火牆出站策略,放行所有UDP,ICMP出站數據包,試試是不是防火牆的問題,再打開tcpdumo抓包
    

在這裏插入圖片描述

  • 發現和防火牆一毛錢關係都沒有,靶機只響應了6個ICMP端口不可達,剩下三個不知道爲啥沒響應,搜了一下發現一篇文章指出了問題,大致意思是缺少了某個函數。
    

在這裏插入圖片描述

  • 看了解決問題的程序,奈何自己太垃圾是用其他語言寫的,看不懂。開始懷疑自己的是不是對UDP協議理解有問題了,回翻看筆記找到了沒收到ICMP不可達的原因,第一和第三都排除了,就剩第二了
    

在這裏插入圖片描述

  •  上網搜了一下ICMP端口不可達響應速率,發現了一篇文章寫了華爲設備對icmp信息的relay速度有限制,目的就是爲了防止被dos佔用設備性能,造成正常服務無法工作。然後再翻了一下自己路由器看到了一個熟悉的菊花logo。
    
  • 	莫名有了種想砸路由器的衝動。迴歸正題,找到問題所在了就可以通過修改代碼來解決問題了。
    

四層主機端口發現

  四層發現指利用OSI中的傳輸層協議進行主機發現,一般使用TCP、UDP探測。
  •  優點:
     1、可以探測遠程主機;
     2、比三層發現更爲可靠
    
  • 缺點:花費時間更長,可能被防火牆過濾
    

探測目標主機是否開放

  1.  向目標主機的相應端口發送UDP數據包,如果沒有返回回覆,則證明這個端口是開放
    
  2. 需要注意的是這點是利用了ICMP不可達的原理,如果目標主機不在線那麼也沒有返回消息,導致誤判,所以要求目標主機必須在線
    
  3. 數據包構造
    
    • result = sr1(IP(dst="192.168.3.107")/UDP(dport=53),timeout=1,verbose=0)
      
    • 最常用的udp端口爲53DNS服務
      

代碼部分

掃描模塊

  • 功能
    
    1.    接收主函數傳過來的IP,端口構造UDP數據包
      
    2.  向端口批量發送數據包,並利用是否有返回消息判斷端口是否在線
      
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()

運行效果
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章