使用Python的Socket模組構建一個UDP掃描工具

發表於2015-06-24

當涉及到對一些目標網路的偵察時,出發點無疑是首先發現宿主主機。這個任務還可能包含嗅探和解析網路中資料包的能力。

幾周前,我曾經談到了如何使用Wireshark來進行資料包嗅探,但如果你沒有wireshark,你如何去監控網路流量呢?

這一次,Python提供了幾種解決方案,今天我將一步步演示如何建立一個UDP主機發現工具。首先,我們要看我們如何處理原始套接字來編寫一個簡單的嗅探器,它能夠檢視和解析網路資料包。然後,我們將在子網內多執行緒執行該程式,結果將在我們的掃描器上。

原始套接字酷之所在是它能夠訪問底層網路的資訊。比如,我們可以用它來檢查IP和ICMP報頭,這是在OSI模型的第三層(網路層)。

使用UDP資料包最酷的事情是:當傳送資訊穿越子網時,不同於TCP,它不太多的開銷(還記得TCP握手吧)。我們需要做的就是等待ICMP迴應,對方主機是否可用或關閉(不可訪問)。記住,ICMP協議本質上是一個特殊的控制協議,它指示錯誤報告和控制機器資料傳輸的的行為。

編寫網路包嗅探器

我們從一個小功能開始:用 Python 的 socket 庫來編寫一個簡單的網路包嗅探器。

在這個嗅探器中,我們建立一個原始 socket 並將它繫結到一個外部網路卡。這個網路卡要啟用混淆模式(promiscuous mode),也就是說獲經過這個網路卡的所有資料包都會被捕獲,包括那些目標地址不是它的資料包。

使用 Windows 時要注意一點:我們需要傳送一個 IOCTL 包才能將網路卡設定為混淆模式。另外,雖然 linux 需要使用 ICMP,Windows 卻可以以一種獨立於協議的方式來嗅探收到的資料包。

在終端中執行如下命令進行測試:

在另一個終端中 ping 或 traceroute 某些地址,如 www.google.com 會得到如下結果:

很明顯需要對這些頭資訊進行解碼。

IP ICMP層解碼

IP 頭

典型的 IP 頭有如下結構,每個欄位都對應一個變數 (這個頭最初是用 C 編寫的):

ICMP頭

同樣,ICMP 由於內容的不同其訊息型別也不同,但每個訊息都包括三個一致的元素:type,code (告知接收主機這個 ICMP 訊息的解碼型別)和 checksum。

對於我們的掃描器,如果得到的 type 和 code 值是3,這意味著 Destination Unreachable(目標不可達)和 Port Unreachable (埠不可達) ICMP 訊息錯誤

為描述 ICMP 訊息頭,我們用 python 的 ctypes 庫來建立一個類

編寫訊息頭解碼器

現在可以著手編寫 IP/ICMP 訊息頭解碼器了。下面的指令碼建立了一個 sniffer socket(正如前面做的那樣),然後在一個迴圈中持續讀取資料包並進行解碼。

注意程式碼中將 IP 頭的前20個位元組讀取到了快取,然後再列印訊息頭的變數。ICMP 頭資料如下:

測試解碼器

在一個終端中執行該指令碼,然後在另一個終端執行一個 ping 命令會得到如下結果(注意 ICMP type 值為 0):

 

另外,如果我們執行 traceroute:

會得到這種輸出 (注意 ICMP 的響應型別):

編寫掃描器

安裝 netaddr

在編寫完整的掃描器前首先要安裝 netaddr,它是一個用於表示和處理網路地址的 python 庫。

Netaddr 提供了操作 IPv4,IPv6 和子網 Mac 等地址的能力。它非常有用,因為我們會用到子網掩碼,如192.168.1.0/24

我們可以使用如下的程式碼段來測試這個庫 (成功會列印“OK”):

深入掃描器

我們會將上面所提到的組織在一起來完成我們的掃描器,然後新增一個迴圈來向目標子網內的所有地址傳送 UDP 資料包。

執行後得到的結果如下:

非常棒!

另外,可以將掃描得到的結果與路由器 DHCP 表中的 IP 地址作對比,它們應該是相同的。

參考:

  • Tutorial to learn netaddr.
  • Black Hat Python.
  • My Gray hat repo.

相關文章