Kate Li (Taiwan)的部落格

首頁

0547 windows dhcp client程式碼執行漏洞分析

作者 urbin 時間 2020-03-10
all

前兩天看了有毒師傅的一個IE瀏覽器堆溢出的漏洞,感覺分析思路很流暢,從漏洞資訊到漏洞分析再到給出檢測方案,就想著按照這個思路分析一下自己最近研究的內容,一個DHCP用戶端的任意代碼執行漏洞。

漏洞描述

CVE-2019-0547,一個Windows系統下DHCP用戶端的任意程式碼執行漏洞,漏洞的主要原因是對DHCP消息的錯誤的處理管道造成記憶體損壞。攻擊者可以通過構造惡意的DHCP響應數据包到存在漏洞的系統中來觸發漏洞,最終可以實現以管理員許可權執行任意程式碼,危害巨大。

漏洞影響範圍

•Microsoft Windows10 version1803•Microsoft Windows Serverversion 1803(ServerCoreInstallation)

該漏洞影響的系統版本只有兩個,但是隨著系統版本反覆運算,現在使用Windows 10的人越來越多,這個漏洞還是需要關注的。

漏洞基本資訊

漏洞觸發檔案:DHCP服務主機上運行的dcpcore.dll漏洞觸發函數:dhcpcore!DecodeDomainSearchListData()漏洞觸發數據對象:一個原本用於存儲域搜索結果的堆緩衝區

漏洞分析

基礎知識

動態主機配置協定(DHCP),主要用於集中管理和自動化網絡上IP地址的分配。它是BOOTP協定的擴展。除了IP地址分配外,DHCP用戶端還從DHCP服務器接收管理其網絡配寘所需的資訊,包括子網路遮罩,閘道地址,DNS伺服器地址等。DHCP使用UDP埠67和68進行通信。DHCP在所有現代作業系統上都是標準的-並且默認情况下已對網路介面啟用-在Microsoft Windows上。

典型的DHCP事務流程如下:

總結上面的過程,DHCP的工作方式如下:在用戶端獲取IP地址之前,它會在本地網絡上廣播DHCP DISCOVER消息。本地網絡上的任何DHCP服務器都可以使用DHCP OFFER響應,其中包含分配給用戶端的IP地址。該IP地址通常是租用的,這意味著它會在一定時間後過期。為了續訂租約,用戶端向DHCP服務器發送單播DHCP REQUEST消息。DHCP服務器以DHCP ACK消息響應。所有DHCP message均以通用的報頭結構開頭。所有多位元組值均以網絡位元組順序排列。該結構描述如下[1]:

通用標頭的長度是固定的,但是後面可以跟可變長度的DHCP選項。每個單獨的DHCP選項具有以下格式:

除了IP地址(包含在“用戶端IP地址”欄位中)之外,DHCP message還使用“option”來包括其他幾個配寘參數,例如“子網路遮罩”(option tag:1),“路由器”(option tag:3),DNS伺服器(option tag:6),NTP服務器(option tag:4),域蒐索(option tag:119)。有關標準option tag的清單,請參見[3]。

該漏洞主要與“域蒐索”選項有關,該選項包含一個或多個DNS尾碼,如果DNS名稱不能自行解析,則用戶端可以使用該尾碼附加到DNS名稱。例如,考慮將分發“example.com”的DHCP服務器作為域蒐索DNS尾碼。如果用戶端向DNS査詢“foo”,但沒有收到任何DNS記錄,則它將繼續査詢“foo.example.com”。使用此功能可避免對網絡內的所有主機重複使用通用組織DNS尾碼。

域搜索選項的選項數據欄位包含wire format的DNS名稱清單。DNS名稱對一個或多個DNS標籤進行編碼,並以終止於零的字元結尾。DNS標籤可以壓縮或不壓縮。未壓縮的DNS標籤是一位元組長度的首碼的八位位元組字串。壓縮標籤是一個兩位元組的無符號整數值,其前兩個最高有效比特設定為1,其餘比特以位元組為組織存儲偏移量。囙此,單個DNS名稱可能由壓縮和未壓縮標籤混合組成。DNS根目錄“.”由單位元組“x00”表示。使用未壓縮的名稱編碼DNS名稱“example.example.com”將變成“x07examplex07examplex03comx00”。可以使用壓縮標籤將其編碼如下:“x07examplexc0x00x03comx00”。有關DNS名稱的更多資訊,請參見[2]。

原理分析

在Windows的DHCP用戶端中存在越界寫漏洞。DHCP用戶端在啟動時作為svchost.exe服務運行,並遵循DHCP協定來獲取系統上網路介面的IP地址。當收到DHCP答覆時,它將使用dhcpcore解析DHCP選項!DhcpExtractFullOptions(),當遇到域搜索選項(option tag:119)時,該調用再調用dhcpcore!DecodeDomainSearchListData()。此函數主要將wire format的DNS名稱轉換為基於文字的DNS名稱。它遍歷每個DNS名稱,在堆上分配記憶體,解壓縮遇到的任何標籤,並使用memcpy()複製標籤,並在標籤之間插入“.”,名稱之間插入”,”。用於存儲DNS名稱的已分配緩衝區大小是基於長度的字串,並且由於DNS名稱以空值結尾,囙此緩衝區大小比DNS名稱小1。囙此,DNS名稱“x07examplex03comx00”導致緩衝區大小為12(請注意,字串的長度為13)。如果DHCP回復消息包含前兩個位元組為零的域搜索選項,則調用程式函數將它們視為兩個不同名稱的兩個以空字元結尾的字元,並將大小為0傳遞給dhcpcore!DecodeDomainSearchListData(),該函數將無法正確驗證,而是調用HeapAlloc分配0位元組的緩衝區。然後,它繼續處理兩個根標籤,並寫入無效緩衝區,從而導致越界寫入。

攻擊者可以設定一個惡意的DHCP服務器並使用惡意的DHCP響應消息來響應同一網段中的DHCP請求,從而利用該漏洞。

程式碼分析

分析使用的dhcpcore.dll版本為10.0.17134.191。

; dhcpcore!DecodeDomainSearchListData: 6ffcb0cc 8bff mov edi,edi 6ffcb0ce 55 push ebp 6ffcb0cf 8bec mov ebp,esp 6ffcb0d1 83ec2c sub esp,2Ch 6ffcb0d4 8bc2 mov eax,edx 6ffcb0d6 894de4 mov dword ptr [ebp-1Ch],ecx 6ffcb0d9 8bc8 mov ecx,eax 6ffcb0db 8945f0 mov dword ptr [ebp-10h],eax 6ffcb0de 53 push ebx 6ffcb0df 8b5d0c mov ebx,dword ptr [ebp+0Ch] 6ffcb0e2 33d2 xor edx,edx 6ffcb0e4 c1e902 shr ecx,2 6ffcb0e7 83c164 add ecx,64h 6ffcb0ea 56 push esi 6ffcb0eb 33f6 xor esi,esi 6ffcb0ed 894dd4 mov dword ptr [ebp-2Ch],ecx 6ffcb0f0 8b4df0 mov ecx,dword ptr [ebp-10h] 6ffcb0f3 83f802 cmp eax,2 6ffcb0f6 57 push edi 6ffcb0f7 8b7d14 mov edi,dword ptr [ebp+14h] 6ffcb0fa 1bc0 sbb eax,eax 6ffcb0fc 40 inc eax 6ffcb0fd 8907 mov dword ptr [edi],eax ; 外层循环开始 6ffcb0ff 833f00 cmp dword ptr [edi],0 6ffcb102 0f8498010000 je dhcpcore!DecodeDomainSearchListData+0x1d4 6ffcb108 42 inc edx ; edx是计数器 6ffcb109 8955f4 mov dword ptr [ebp-0Ch],edx ; 第一次迭代时跳过HeapFree 6ffcb10c 83fa02 cmp edx,2 6ffcb0fd 8907 mov dword ptr [edi],eax 6ffcb0ff 833f00 cmp dword ptr [edi],0 ; 第二次迭代, HeapFree and HeapAlloc都发生了. 6ffcb102 0f8498010000 je dhcpcore!DecodeDomainSearchListData+0x1d4 6ffcb108 42 inc edx 6ffcb109 8955f4 mov dword ptr [ebp-0Ch],edx 6ffcb10c 83fa02 cmp edx,2 6ffcb10f 7533 jne dhcpcore!DecodeDomainSearchListData+0x78 6ffcb111 8b7508 mov esi,dword ptr [ebp+8] 6ffcb114 833e00 cmp dword ptr [esi],0 6ffcb117 7413 je dhcpcore!DecodeDomainSearchListData+0x60 6ffcb119 ff36 push dword ptr [esi] 6ffcb11b 6a00 push 0 6ffcb11d ff35580dff6f push dword ptr [dhcpcore!DhcpGlobalHeap] 6ffcb123 ff155c21ff6f call dword ptr [dhcpcore!_imp__HeapFree] 6ffcb129 832600 and dword ptr [esi],0 ; [ebx]是该函数的参数。 DNS名称的累积长度; 排除终止空字节. 6ffcb12c ff33 push dword ptr [ebx] ; dwBytes (size) In malicious case ebx = 0 6ffcb12e 6a08 push 8 ; dwFlags 6ffcb130 ff35580dff6f push dword ptr [dhcpcore!DhcpGlobalHeap] ; hHeap 6ffcb136 ff155021ff6f call dword ptr [dhcpcore!_imp__HeapAlloc] ; HeapAlloc 0 bytes 6ffcb13c 8b55f4 mov edx,dword ptr [ebp-0Ch] 6ffcb13f 8bf0 mov esi,eax ; esi = buffer allocated.size不能为0 6ffcb141 8b4df0 mov ecx,dword ptr [ebp-10h] 6ffcb144 8b4510 mov eax,dword ptr [ebp+10h] 6ffcb147 832300 and dword ptr [ebx],0 6ffcb14a 8365f800 and dword ptr [ebp-8],0 6ffcb14e 832000 and dword ptr [eax],0 6ffcb151 33c0 xor eax,eax 6ffcb153 8945ec mov dword ptr [ebp-14h],eax 6ffcb156 8845ff mov byte ptr [ebp-1],al 6ffcb159 85c9 test ecx,ecx 6ffcb15b 0f8419010000 je dhcpcore!DecodeDomainSearchListData+0x1ae ;内层循环开始 6ffcb161 8b07 mov eax,dword ptr [edi] 6ffcb163 85c0 test eax,eax 6ffcb165 0f840c010000 je dhcpcore!DecodeDomainSearchListData+0x1ab 6ffcb16b 8365e800 and dword ptr [ebp-18h],0 6ffcb16f 8b4dec mov ecx,dword ptr [ebp-14h] 6ffcb172 8b5de4 mov ebx,dword ptr [ebp-1Ch] 6ffcb175 03d9 add ebx,ecx 6ffcb177 895de0 mov dword ptr [ebp-20h],ebx 6ffcb17a 8a1b mov bl,byte ptr [ebx] 6ffcb17c 885dfe mov byte ptr [ebp-2],bl 6ffcb17f 84db test bl,bl 6ffcb181 8b5d0c mov ebx,dword ptr [ebp+0Ch] ; 第一次迭代时跳转. 6ffcb184 0f8492000000 je dhcpcore!DecodeDomainSearchListData+0x150 6ffcb21c 807dff00 cmp byte ptr [ebp-1],0 6ffcb220 7605 jbe dhcpcore!DecodeDomainSearchListData+0x15b 6ffcb222 8b4510 mov eax,dword ptr [ebp+10h] .......... 6ffcb267 8b45ec mov eax,dword ptr [ebp-14h] 6ffcb26a 8b55f4 mov edx,dword ptr [ebp-0Ch] 6ffcb26d 3bc1 cmp eax,ecx ; 跳转到第一次迭代的内层循环的开始位置 6ffcb26f 0f82ecfeffff jb dhcpcore!DecodeDomainSearchListData+0x95 ; 继续第二次迭代 6ffcb275 eb03 jmp dhcpcore!DecodeDomainSearchListData+0x1ae 6ffcb277 8b45ec mov eax,dword ptr [ebp-14h] 6ffcb27a 8b55f4 mov edx,dword ptr [ebp-0Ch] 6ffcb27d 3bc1 cmp eax,ecx 6ffcb27f 7405 je dhcpcore!DecodeDomainSearchListData+0x1ba 6ffcb281 832700 and dword ptr [edi],0 6ffcb284 eb0c jmp dhcpcore!DecodeDomainSearchListData+0x1c6 6ffcb286 83fa02 cmp edx,2 6ffcb289 750a jne dhcpcore!DecodeDomainSearchListData+0x1c9 6ffcb28b 8b03 mov eax,dword ptr [ebx] ;发生越界写,程序可能不会在这里崩溃,因为单个字节被写入esi-1,这可能是一个有效的地址。 6ffcb28d c64430ff00 mov byte ptr [eax+esi-1],0 6ffcb18a 8a55fe mov dl,byte ptr [ebp-2] 6ffcb18d 80fac0 cmp dl,0C0h ; dhcpcore!ClearDomainSearchOption: 6ffcb025 8bff mov edi,edi 6ffcb027 55 push ebp 6ffcb028 8bec mov ebp,esp 6ffcb02a 51 push ecx 6ffcb02b 56 push esi 6ffcb02c 8bf1 mov esi,ecx 6ffcb02e 57 push edi 6ffcb02f 33ff xor edi,edi ;来自上述HeapAlloc的无效地址(esi) 6ffcb031 8b8624070000 mov eax,dword ptr [esi+724h] 6ffcb037 85c0 test eax,eax 6ffcb039 7414 je dhcpcore!ClearDomainSearchOption+0x2a 6ffcb03b 50 push eax 6ffcb03c 57 push edi 6ffcb03d ff35580dff6f push dword ptr [dhcpcore!DhcpGlobalHeap] ;在无效堆上释放导致svchost.exe进程崩溃 6ffcb043 ff155c21ff6f call dword ptr [dhcpcore!_imp__HeapFree] 6ffcb049 89be24070000 mov dword ptr [esi+724h],edi

攻擊場景分析

攻擊者可以構造一個惡意的DHCP服務器,充分發揮該漏洞的作用需要進入到某一個網段內,然後監聽網段內的DHCP請求,接收到請求之後就響應一個特製的惡意的DHCP響應數據,這樣就可以造成存在漏洞的系統執行程式碼。

流量分析

首先client發起一個request:

然後,攻擊者構建的惡意DHCP服務器響應一個惡意的ACK消息:

在響應包中可以明顯看到觸發漏洞的惡意數據。

檢測思路

首先要監聽UDP的67/68埠的流量。檢測設備可以根據DHCP Magic Cookie值x63x82x53x63來判斷是否為DHCP消息。如果操作碼為2,則檢測設備必須解析每個DHCP選項,並檢查option tag設定為0x77的所有選項的選項數據。如果發現任何此類選項的選項數據以“x00x00”開頭,則應將流量視為可疑的攻擊流量。

總結

這個分析思路很清楚,基本上是一個漏洞響應的微縮過程,到最後給出解決方案,個人感覺比較成熟。最後的流量檢測現在很多的防火牆都可以實現,從流量側攔截攻擊好過主機防禦。

參考文獻

[1] RFC 2131,Dynamic Host Configuration Protocol https://tools.ietf.org/html/rfc2131[2] P. Mockapetris,RFC 1035: DOMAIN NAMES–IMPLEMENTATION AND SPECIFICATION,https://tools.ietf.org/html/rfc1035[3] IANA,Dynamic Host Configuration Protocol(DHCP)and Bootstrap Protocol(BOOTP)Parameters,https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml