CVE-2012-3569 VMware OVF Tool格式化字符串漏洞分析
漏洞信息
1、漏洞簡述
- 漏洞名稱:VMware OVF Tool格式化字符串漏洞
- 漏洞編號:CVE-2012-3569
- 漏洞類型:格式化字符串漏洞
- 漏洞影響:信息泄露
- CVSS評分:9.3(High)
- 利用難度:Medium
- 基礎權限:不需要
2、組件概述
開放虛擬機格式(Open Virtual Machine Format,OVF)是一種虛擬機分配格式,能夠支持不同產品與組織之間共享虛擬機。
VMware OVF Tool是由VMware免費提供的一款支持虛擬的導入導出工具,支持以命令提示符的方式運行。
3、漏洞影響
VMware官網提供的影響版本。

4、解決方案
更新軟件。
官方文檔鏈接
(https://www.vmware.com/security/advisories/VMSA-2012-0015.html)
漏洞分析
1、基本信息
- 漏洞文件:poc.ovf、exp.ovf(《漏洞戰爭》配套資料)
- 漏洞函數:字符串打印函數std::basic_ostream
- 漏洞對象:ovf文檔
2、背景知識
(1) 格式化字符串漏洞
一段簡單的代碼:
#includeint main(){ char a[]="asdasd"; printf(a); return 0;}
其輸出為:
asdasd
上面的代碼不會有什么問題,但是如果將字符串的輸入權交給用戶就會有問題了。看下面的代碼:
#include int main(){ char a[100]; scanf("%s",str); printf(str); return 0;}
如果用戶輸入的字符串是"%x%x%x",則會輸出以下結果:
6efe4c6effcc753eca20
可以看到程序打印了一堆斷碼,這其中的原因是什么呢,先要從棧的布局說起:
函數調用棧的簡化布局如圖所示(其實棧中還有一些其他無關緊要的數據,筆者在圖中沒有畫出):

正常情況下輸出函數的語句應該是(前提是a、b、c已經賦值):
printf(str,a,b,c)
這時棧中結構如下,程序在處理字符串時發現了三個%x,就會在棧中檢索三個變量的位置:

如果沒有輸入這三個變量,程序同樣會向下檢索三個“變量”,棧中布局如下:

可以看到,我們竟然成功騙過程序,讓其向下訪問棧的地址空間,從而造成了內存泄漏。
理解基本原理后,我們看看其他格式化控制符:
%s :這個控制字符用于打印字符串,其原理是訪問存放指向字符串的指針,進而從指針指向的位置打印“字符串”,直至打印到0x00這個截斷符。
棧中的局部變量緩沖區存放字符串本身,而利用多個%s可以訪問到局部變量緩沖區,如果輸入的字符串是某個地址加上很多%s,當某一個%s訪問到緩沖區字符串時,就會認為這個地址指向字符串,并打印這個地址空間的內容直至截斷符,這樣就做到了任意地址空間的訪問。
%n 和 %hn :%n是一個很特殊的格式化控制符,其功能不是打印,而是將目前已打印的字符個數寫入對應的變量,如果觸發格式化字符串漏洞,%n會將棧中某個位置的數據修改為目前打印出來的字符的個數,%n是以DWORD的形式寫入,%hn以WORD形式寫入。通過控制輸出字符的數量和%n在字符串中的位置,可以達到修改棧中的任意數據的效果,甚至是EBP,返回地址等關鍵數據。
(2)跳板技術
從匯編角度講,在函數執行retn返回前,ESP剛好指向棧中存放返回地址的+0x4地址,若是發生棧溢出,將函數的返回地址覆蓋為jmp/call esp的地址,剩下的部分覆蓋為shellcode,程序就會在返回時自動跳轉到shellcode地址,這種方法能夠增加shellcode的寫入空間,并且繞過ASLR。
3、詳細分析
(1)基礎分析
參考《漏洞戰爭》及其配套資料,安裝VMware OVF Tool 2.1.0,將poc.ovf和exp.ovf放到安裝路徑中,命令行進入安裝路徑。
輸入命令voftool poc.ovf:


可以看到終端先打印了一堆字符串后,系統報出內存訪問錯誤,查看詳細報告:

報告中顯示程序訪問了錯誤的地址0x00000000。
在010 Editor中查看poc.ovf文件:

能夠看到文件中存在一大段字符串,并且存在大量的格式控制符%08x,用于打印8位十六進制數(高位填充0)。
(2)靜態分析
Ida分析
使用Ida打開ovftool,查看std::basic_ostream函數所在的源碼:
v36 = v30[5];v20 = (int (__thiscall **)(_DWORD, _DWORD, _DWORD))(*(_DWORD *)v30[5] + 16);v21 = (*(int (__thiscall **)(int, int))(*(_DWORD *)v4 + 44))(v4, v19);v22 = (*v20)(v36, &v28, v21);v31 = 1;v23 = v22;v24 = sub_401A90(&dword_160C7D8, " - ");v25 = std::operator<<,std::allocator>(v24, v23);std::basic_ostream>::operator<<(v25, std::endl); //此處沒有對參數進行過濾,導致格式化字符串漏洞v31 = -1;std::basic_string,std::allocator>::~basic_string,std::allocator>(&v28);++v19;result = (*(int (__thiscall **)(_DWORD))(*(_DWORD *)v4 + 36))(v4)
可以看到std::basic_ostream函數沒有對參數進行過濾,導致格式化字符串漏洞。
(3)動態分析
①分析poc.ovf
啟動OD,設置調試-參數,輸入poc.ovf,打開ovftool運行至結束,由于剛才的輸入信息中包含Invalid value字符串,在反匯編窗口中右鍵-查找-所有參考文本字串:

找到“Invalid value”,雙擊轉到引用字符串的命令,并在其附近設下斷點:

可以看到附近的函數都是C++的std,繼續F8運行數次后能夠找到std::basic_ostream這個C++打印函數。

查看當前函數的EBP鏈:

可以看到整個函數調用棧的EBP鏈分別為:
0012FC1C - 0012FC74 - 0012FF50 - 0012FF7C - 0012FFC0 - 0012FFF0
單步步過這個函數后在查看EBP鏈:

0012FC1C - 0012FC74 - 0012FF50 - 00120345
可以明顯看到0012FF50這個EBP指向的上一個棧的棧幀被修改為00120345,初步判斷是由于格式化字符串造成的,打開101 Editor統計字符串。
"AAAAAAAAAAAAAAAAAAAAAAAAAA%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%hn"
通過統計得出字符串中共有26個'A',98個%08x和1個%hn,在%hn之前打印出的字符數量應為26 + 98*8 = 810,810的十六進制數剛好是0x345。通過%hn將EBP的第四位賦值為0345,使得EBP由0012FF7C被覆蓋為00120345。
繼續跟進知道EBP被賦值為00120345:

可以看到程序執行完00417AEF處的指令pop ebp后,EBP的值被覆蓋為00120345,繼續運行:

運行后ESP也被賦值為00120345,而此時00120345處的地址內容為00000000,在接下來的retn指令執行后,eip被修改為00000000,從而導致系統訪問00000000這個無效地址,造成報錯。
② 分析exp.ovf
與分析poc.ovf分析方式類似,將程序執行至std::basic_ostream這個函數,查看EBP鏈:

EBP鏈為:
0012FC1C - 0012FC74 - 0012FF50 - 0012FF7C - 0012FFC0 - 0012FFF0
步過函數后:

可以看到EBP鏈被修改為:
0012FC1C - 0012FC74 - 0012FF50 - 00121000
EBP已經被修改為00121000,繼續運行至ESP被修改為00121000:

繼續運行pop ebp后,ESP值變為00121004,retn后eip被修改為00121004處的內容,查看00121004存放的地址7852753D對應的對應的反匯編:

反匯編為call esp,是一個跳板指令,能夠將程序執行流劫持到00121008,在數據窗口中定位121008:

能夠看到大量的字符串,將向上查找能夠找到字符串的開始“znHt...”。


繼續運行此處發現出現循環,并且內存中的字符串逐漸變為不可打印的字符:

說明shellcode正在進行解碼操作,繼續跟進后寄存器窗口中出現相關函數及其參數,并跳出對話框。

緩解措施
更新軟件。