MS-17-010:EternalBlue對SRV驅動程式的Large Non-Paged Pool溢位

EternalBlue的漏洞攻擊碼在五月成為受人注目的焦點,因為它和過去數周出現的大規模惡意軟體攻擊大有關聯 – 知名的 WannaCry(想哭) 勒索蠕蟲、無檔案勒索病毒UIWIXSMB蠕蟲 EternalRocks以及電子貨幣採礦惡意軟體Adylkuzz。

EternalBlue(微軟釋出MS17-010修補)是關於Windows SMB 1.0(SMBv1)伺服器如何處理特定請求相關的安全漏洞。一旦攻擊成功,就可以讓攻擊者在目標系統上執行任意程式碼。EternalBlue及其他被駭客集團Shadow Broker所流出漏洞攻擊碼的嚴重性和複雜性被認定為中到高的程度。

趨勢科技進一步的研究EternalBlue內部運作來更加地了解此漏洞攻擊如何運作,並提供關於此漏洞攻擊肆虐世界各地不同產業組織的技術見解。

《延伸閱讀》與 WannaCry 較勁?!「EternalRocks」一口氣用同個駭客集團外流的七個漏洞展開攻擊

 

漏洞分析

Windows SMBv1的核心函式srv!SrvOs2FeaListToNt在處理檔案延伸屬性(FEA)時,Large Non-Paged核心集區記憶體(kernel pool memory)存在緩衝區溢位的漏洞。srv!SrvOs2FeaListToNt在將FEA列表轉換成NTFEA(Windows NT FEA)列表前會呼叫srv!SrvOs2FeaListSizeToNt來計算所接收FEA列表的大小。按順序進行下面操作:

  1. srv!SrvOs2FeaListSizeToNt會計算FEA列表大小和更新所接收FEA列表大小
  2. 因為錯誤的WORD類型轉換造成FEA大小比原值大
  3. 當FEA列表重複地被轉換為NTFEA列表時,非分頁集區(non-page pool)會出現溢位,因為原本的總列表大小被誤算

 

溢位分析

我們的溢位分析是根據srv.sys 6.1.7601.17514_x86。有漏洞的程式碼可以用srv!SrvSmbOpen2觸發。追踪如下:

00 94527bb4 82171149 srv!SrvSmbOpen2

➜ SrvOs2FeaListSizeToNt()

01 94527bc8 821721b8 srv!ExecuteTransaction+0x101

02 94527c00 8213b496 srv!SrvSmbTransactionSecondary+0x2c5

03 94527c28 8214a922 srv!SrvProcessSmb+0x187

04 94527c50 82c5df5e srv!WorkerThread+0x15c

05 94527c90 82b05219 nt!PspSystemThreadStartup+0x9e

06 00000000 00000000 nt!KiThreadStartup+0x19

為了分析溢位,我們設置了中斷點:

bp srv!SrvSmbOpen2+0x79 “.printf \”feasize: %p indatasize: %p fealist addr: %p\\n\”,edx,ecx,eax;g;”

程式中斷後會得到以下數值(十六進制,十進制):

  • feasize:00010000(65536)
  • indatasize:000103d0(66512)
  • fealist addr:89e980d8

 

從這裡可以看到IN-DATA大小為66512,和NT Trans Request的Total Data Count相同,比FEA列表大小65536來得大。

https://blog.trendmicro.com/trendlabs-security-intelligence/files/2017/06/figure1_eternalblue.jpg

 

圖1:顯示IN-DATA大小(用藍色標出)和FEA列表大小(用紅色標出)的代碼截圖

 

這裡值得注意的是,IN-DATA會被轉換成如下的FEA列表結構:

 

圖2:FEA列表結構

 

轉換IN-DATA之後,我們會得到FEA大小00010000(65536),存放在FEALIST➜cblist。SMB驅動程式下一步會分配一個緩衝區(buffer)來將FEA列表轉換成NT FEA列表。這代表它要計算NTFEA列表的大小,透過呼叫srv!SrvOs2FeaListSizeToNt函式完成。

 

為了查看此函式的返回值,我們設置以下中斷點:

bp srv!SrvOs2FeaListToNt+0x10 “.printf \”feasize before: %p\\n\”,poi(edi);r $t0 = @edi;g;”

bp srv!SrvOs2FeaListToNt+0x15 “.printf \”NTFEA size: %p feasize after: %p\\n\”,eax,poi(@$t0);g;”

 

中斷後得到:

  • 之前的feasize:00010000
  • 之後的feasize:0001ff5d
  • NTFEA大小:00010fe8

我們發現FEALIST➜  cblist從0x10000更新到0x1ff5d。但是哪部分的程式碼造成計算錯誤?下面程式碼會顯示錯誤如何發生:

 

圖3:顯示計算FEALIST ➜  cblist錯誤的程式碼截圖

 

根據上面的截圖,40行起顯示出了計算錯誤。因為原本的FEA列表大小被更新,重複複製數值到NTFEA列表會超過在v6返回的NTFEA大小(00010fe8)。請注意,如果函式在28或21行返回,FEA列表不會被更新。另一個在EternalBlue以外會導致v1更新的情況是FEA列表末端資料不足以儲存另一個FEA結構。

我們也分析了當LARGE NON-PAGE Kernel Pool出現緩衝區溢位時,核心記憶體會發生什麼事。當SrvOs2FeaListSizeToNt返回數值,需要用來儲存NTFEA列表的大小是00010fe8。這需要SRV.sys分配一個Large Kernel POOL。使用下面的中斷點來幫助追蹤當FEA列表轉換成NTFEA列表時究竟發生了什麼事:

bp srv!SrvOs2FeaListToNt+0x99 “.printf \”NEXT: FEA: %p NTFEA: %p\\n\”,esi,eax;g;”

bp srv!SrvOs2FeaToNt+04d “.printf \”MOV2: dst: %p src: %p size: %p\\n\”,ebx,eax,poi(esp+8);g;”

 

bp srv!SrvOs2FeaListToNt+0xd5

 

概略地說,一旦SrvOs2FeaListSizeToNt被呼叫而集區(Pool)也已經分配,會使用函式SrvOs2FeaToNt來將FEA列表內所有的元素加以轉換。在SrvOs2FeaToNt內有兩個_memmove操作,所有的緩衝區複製操作都會在這發生。透過以上的中斷點,可以追踪FEA列表轉換時發生什麼事。追踪需要花上相當的時間。

圖4:顯示複製操作的程式碼截圖

 

當停在中斷點srv!SrvOs2FeaListToNt+0xd5時,我們可以取得分析緩衝區溢位所需的所有資料。605複製操作拷貝了0字元,這是因為在有效載荷開頭,FEA列表會有0位元的值,對應到605 FEA結構。下一個FEA大小會是F383(複製606),最後複製會在85915ff0結束。

在複製操作606後,我們會看到緩衝區:85905008 + 10FE8 = 85915FF0。然而,會出現另一次的FEA迭代,在此例中的大小為A8。這會覆蓋下一個記憶體區。注意在覆蓋資料後會出現在不同的集區(pool),在此例中為SRVNET.sys pool。

複製操作607後是已損壞的FEA,伺服器返回STATUS_INVALID_PARAMETER(0xC000000D)。NT Transaction最後的FEA發送到伺服器。

圖5:顯示損壞的FEA和伺服器返回值的程式碼截圖

 

EternalBlue的漏洞攻擊能力

 

溢位發生在NON-PAGED Pool記憶體,明確地說是Large NON-PAGED Pool。Large non-page pool沒有POOL標頭。因為如此,在Large Pool緩衝區後可以分配另一個集區緩衝區(POOL buffer) – 這是帶有特定DRIVER資料的驅動程式所擁有。

因此,攻擊需要在緩衝區溢位後操縱傳來的集區緩衝區(POOL buffer)。EternalBlue的作法是控制SRVNET驅動程式緩衝區結構。為了做到這一點,兩個緩衝區需要在記憶體內對齊。要建立非分頁集區(NON-PAGED POOL)對齊,需要噴射(spray)核心集區(kernel pool),作法如下:

  1. 建立多個SRVNET緩衝區
  2. 釋放一些緩衝區來建立一些空洞(hole)讓SRV緩衝區被複製
  3. 發送SRV緩衝區讓SRVNET緩衝區溢位。

 

漏洞攻擊機制

有緩衝區溢位漏洞的程式碼在KERNEL NON-PAGED記憶體運作。它也可以在LARGE NON-PAGED POOL運作。這些集區(Pool)在分頁(page)開頭沒有嵌入任何POOL標頭,所以需要特殊作法去攻擊。這作法需要逆轉一些結構來分配到溢位區,如下圖所示:

圖6:EternalBlue的漏洞攻擊機制

 

建立多個SRVNET緩衝區跟記憶體內所發生的事情近似,只是用來表現這個想法。請注意,我們有意地省略部分細節以防止我們的分析被濫用。

圖7:EternalBlue的漏洞攻擊鏈

 

EternalBlue的漏洞攻擊鏈

 

EternalBlue經過一連串的程序來成功地攻擊有漏洞的系統或網路,如上圖所示。

EternalBlue首先發送一個SRV緩衝區(除了最後一個封包)。這是因為當交易(transaction)的最後一筆資料到達伺服器時,Large NON-PAGED POOL緩衝區會被建立。SMB伺服器會將資料積累在輸入緩衝區,直到所有交易資料都被讀取。總交易資料會被指定在初始TRANS封包。一旦所有交易資料抵達,SMB伺服器會處理資料。在此例中,資料被分派到SrvOpen2函式來讀經由網路文件共享系統(CIFS)的資料。

在此時,EternalBlue確保所有發送資料被伺服器接收並送出SMB ECHO封包。因為攻擊可以透過較慢的網路進行,這echo指令相當重要。

在我們的分析中,即使我們送出初始資料,有漏洞的緩衝區還沒有在記憶體內建立。要用下列步驟來試著在SRVNET緩衝區前分配一個SRV有漏洞的緩衝區:

  1. FreeHole_A:EternalBlue開始發送SMBv1封包來建立一個kernel hole A
  2. SMBv2_1n:發送一組SMBv2封包
  3. FreeHole_B:發送另一個空的hole buffer;這需要在前一個hole釋放前發送,以確保另外一個會建立
  4. FreeHole_A_CLOSE:關閉連線讓緩衝區釋放,之後關閉A以建立空的hole
  5. SMBv2_2n:發送一組SMBv2封包
  6. FreeHole_B_CLOSE:關閉連線讓緩衝區釋放
  7. FINAL_Vulnerable_Buffer:發送有漏洞緩衝區的最後一個封包

 

一個有漏洞的緩衝區會被建立在記憶體內,就在SRVNET緩衝區前,部分SRVNET會被覆蓋。FEA列表轉換成NTFEA列表會返回錯誤,因為FEA結構在某一點後會無效,在此時伺服器會返回STATUS_INVALID_PARAMETER(0xC000000D)。

 

修補系統

EternalBlue成了許多惡意軟體的入場卷,嚴重地影響了全球的企業和一般用戶,也讓人們再次認識到安裝最新修補程式和保持系統與網路更新的重要性。EternalBlue已經讓Windows系統發布了修補程式,包括了停止支援的作業系統

除了要進行系統和網路的定期更新,也建議IT/系統管理員要採用最佳實作,例如啟用入侵偵測和預防系統,停用過期或不必要的協定和端口(如445),主動監視網路流量,防護端點和部署安全機制(如資料分類及網路分區)以減少暴露面所帶來的破壞。採用虛擬修補也有助於對抗未知漏洞。

 

趨勢科技解決方案

 

趨勢科技的Deep SecurityVulnerability Protection提供虛擬修補技術來保護端點對抗無檔案病毒和漏洞攻擊等威脅。OffieScan的Vulnerability Protection防護端點封鎖已知和未知的漏洞攻擊,甚至在修補程式釋出之前提供保護。

趨勢科技的Deep Discovery能夠偵測、深入分析及主動回應漏洞攻擊及其他類似威脅。透過特製引擎、客製化沙箱及橫跨整個攻擊生命週期的無縫關聯技術,讓它即使沒有更新引擎或病毒碼也能夠偵測威脅。

可以在以下技術支援網頁找到趨勢科技對EternalBlue及利用其漏洞攻擊碼的惡意軟體所提供解決方案的更深入資訊:

 

@原文出處:MS-17-010: EternalBlue’s Large Non-Paged Pool Overflow in SRV Driver 作者:William Gamazo Sanchez(漏洞研究員)