vmware漏洞之二——簡評:實戰VMware虛擬機逃逸漏洞

下文取自360,是vmware exploit做者本身撰寫的。本文從實驗角度對做者的文章進行解釋,有助於學習和理解。文章虛線內或紅色括號內爲本人撰寫。html

----------------------------------------------------------------python

轉:http://bobao.360.cn/learning/detail/4143.htmllinux

做者:skyergit

0x00 前言github


最近長亭把Pwn2Own中遺憾的在比賽前一天被補上的漏洞利用發了出來,Amat大佬的博客有這篇文章,同時在長亭知乎專欄有楊博士發的中文版。 可是並無公開的exp,如何真正實現呢?本身花了十幾天才寫出exp,其中踩坑無數,本着分享精神,因而就有了本文。shell

0x01 Backdoorc#


backdoor是vmware實現的一套Guest和Host通訊的機制,咱們不須要去深刻研究這種機制如何實現的,只須要大概瞭解一下這個機制的實現。 先看通訊的代碼,這部分代碼在open-vm-tools的github上也有,連接在此。因爲須要在VS中編譯,因此須要先轉換成爲intel的asm格式。windows

http://p1.qhimg.com/t01a8f3046a1c9e56b9.png

在正常操做系統中直接執行in指令會致使出錯,由於這是特權指令。可是在Guest中這個錯誤會被vmware捕獲,而後傳給vmware-vmx.exe進程內部進行通訊。 app

然後面咱們須要操做的message,所有經過backdoor通訊方式來通訊。 關於message的操做,open-vm-tools裏面也有相關實現,連接在此。直接拿過來用就好了。 有了Message_Send和Message_Recv這些函數,咱們就能夠直接在Guest裏面與Host進程進行通訊。 須要注意的是Backdoor通訊在Guest內部不須要管理員權限,因此此bug可在普通用戶觸發。函數

 

0x02 Drag and Drop RPCI

 


RPCI是基於backdoor實現的通訊方式。open-vm-tools相關實如今此。能夠直接使用這個發送RPC的函數。 這個漏洞存在在DnD操做的v3版本代碼中,對應bug代碼在此。

ida中更加明顯: 

http://p0.qhimg.com/t01c75c211912d49386.png

----------------------------------------------------------------------------------

我的分析狀況:

char __fastcall my_TransportBufAppendPacket_4F0540(__int64 a1, struct_v5 *a2, unsigned __int64 a3)
{
struct_v3 *v3; // rbx@1
__int64 v4; // rcx@1
struct_v5 *v5; // rdi@1 v5爲發送的包。//IDA直接分析時沒有結構體,須要右鍵自動建立
int v6; // eax@3
unsigned int v7; // edx@3
__int64 v8; // rax@9
__int64 v9; // rcx@10
char result; // al@11

v3 = (struct_v3 *)a1;
v4 = a2->payload_size;
v5 = a2;
if ( a3 != v4 + 20 )
  goto failed;
if ( a3 > 0xFF9C )
  goto failed;
v6 = a2->offset;
v7 = a2->totalsize;
if ( v6 + (signed int)v4 > v7 || v7 > 0x3FFFF3 )
  goto failed;
if ( v3->seq != v5->seq )
  sub_4F0430(v3);
if ( !v3->buffer ) // 第一次分配。
{
  if ( v5->offset )
    goto failed;
  v3->buffer = my_malloc_3D4A90(v5->totalsize);// 以totalsize爲準
  v3->totalsize = v5->totalsize;
  v8 = v5->seq;
  v3->offset = 0i64;
  v3->seq = v8;
}
v9 = v3->offset;
if ( v9 == v5->offset )
{
  memcpy((char *)v3->buffer + v9, &v5->payload, v5->payload_size);// 用戶更改binarysize爲很大。
  result = 1;
  v3->offset += v5->payload_size;
  return result;
}
failed:
  free(v3->buffer);
  v3->buffer = 0i64;
  v3->seq = 0i64;
  v3->totalsize = 0i64;
  v3->offset = 0i64;
  v3->qword20 = 0i64;
  return 0;
}

----------------------------------------------

因爲沒有realloc或者totalsize的判斷,致使第二個包的totalsize能夠改爲一個大值,payloadsize所以也能夠變大致使一個堆溢出。

順帶一提,發送DnD操做的命令在dndCPTransportGuestRpc.hpp中。 經過閱讀open-vm-tools的代碼,能夠得出RPC的發送對應路徑: 

rpcv3util::SendMsg->DnDCPTransportGuestRpc::SendPacket->RpcChannel_Send->Message_Send->backdoor

 

0x03逆向分析


看完相關的open-vm-tools的代碼以後,開始逆向vmware-vmx.exe,個人版本是12.5.2.13578,workstation是12.5.2-build4638234版本。 

首先很容易經過字符串「tools.capability.dnd_version」的xref找到對應的處理函數。

my_rpcicmd_func_map_8A140()

 http://p3.qhimg.com/t012527b12791d74463.png

bindfun只是把對應的參數值寫入了全局變量,實際上是一個表。bindfun參數4就是對應rpc命令的處理函數,而rpc命令函數的參數3和參數4分別是咱們發送的RPC原始request和RPCrequest的長度。參數5和參數6是咱們獲得的 reply的地址和reply的長度。 

http://p4.qhimg.com/t0156d1580181144a54.png

能夠看出這個命令有一個參數,也就是版本號。

其餘的RPC命令相似,在發送「vmx.capability.dnd_version」命令的時候,對應的處理函數中若是發現當前版本和設置的版本不一致,就會調用函數建立新的 object,把原來的版本的object銷燬。

my_vmx_capability_dnd_83EE0

 http://p0.qhimg.com/t01910513930f85086e.png

 http://p5.qhimg.com/t0195e752837dc0ee40.png

DnD和CP的Object的size都是同樣的,都是0xa8大小。vmware_vmx+9C470

 http://p0.qhimg.com/t01a4992c613558749b.png

 

0x04 漏洞利用


Amat大佬的文章中推薦用info-set和info-get來操做堆,其中info-set對應的handle函數內部很複雜,經過windbg動態調試,能夠發現咱們發送「info-set guestinfo.test1 「+’a'*0xa7能夠建立一個0xa8大小的buffer。實際測試我在malloc和free下斷點,整個info-set過程大概有10-13次malloc(size=0xa8),也有 接近10次的free操做,最終剩下一個buffer也就是說整個info-set過程干擾很大

 info-get能夠讀取剛剛set的值,這就沒什麼好說。 關於windows的LFH的風水,因爲info-set中有屢次malloc 0xa8操做,因此比較困難。我沒有什麼好的辦法,目前我exp成功率仍是比較低。 

思路大概就是把內存變成這個樣子:

http://p0.qhimg.com/t01befb96211e1c4980.png

若是一旦沒有佈局成功。。vmware-vmx就會崩潰。。。 

若是你正好掛了windbg調試器。那麼整個host就會其卡無比(暫時沒法解決。Ollydbg,immunity只適合於32位。vmware12只有64位。不過linux環境下用gdb調試竟然不卡!)。未知bug。只能緩慢的對windbg調試器按q退出調試。 

推薦安裝windbg的pykd插件,大愛python。 我寫了個小腳本用來輔助調試:(其實就是打印rax)--(也能夠直接用windbg打印)

1
2
3
4
5
6
from  pykd  import  *
import  sys
s = ''
if  len (sys.argv)> 1 :
s = sys.argv[ 1 ] + ' '
print  s + 'Object at ' + hex (reg( 'rax' ))

因此就能夠在attach上vmx進程的時候這麼輸入:

1
2
3
bp 7FF7E394C4D8 "!py dumprax DnD;gc;";bp 7FF7E394BF68 "!py dumprax 
CP;gc;";bp 7FF7E3DA05AB "!py dumprax vuln;gc;";bp 7FF7E3DA05DB;bp 
7ff7e38c1b2d;bp 7ff7`e38f1dc2;g

第一個地址是DnD Object malloc完畢後的下一條指令,第二個地址是CP Object的,第三個是vuln的,第四個地址是memcpy觸發的地方,後面兩個是gadget地址。

由於windows中進程重啓後基地址仍是不會變,因此只要你不重啓電腦,能夠一直用。

經過一些佈局(運氣)變成了如上的內存以後,就能夠開始leak了。

主要是經過覆蓋info-set的value buffer,修改value buffer內部的值,若是此時info-get讀取的valuebuffer值不一樣,那就說明被覆蓋了。

而若是溢出到了Object頭部,從info-get讀取的信息就會包含vtable的地址,從而泄露出程序基地址。

固然這個過程當中有可能觸發RtlHeapFree等堆函數然。。由於堆chunk頭被覆蓋,理所固然崩潰。。。

--------------------------------------------------------------------------

目前來當作功率越爲30%左右。不過跟環境可能有關係,一次將虛擬機大小設爲2G,雙核,命中率極低;將大小改成512MB,單核,命中率達到30%。

在ASLR完成後,能夠經過"s -a 0 L8000000 "exploit test"來定位dnd/cp對象。

-------------------------------------------------------------------------- 

0x05 DnD Object 覆蓋


若是覆蓋的是DnD Object,那麼在DnD_TransportBufAppendPacket(vmware_vmx+4F0540函數結束以後的上層函數(vmware_vmx+4FE960)會馬上發生調用。

http://p4.qhimg.com/t015a03b1d4a70c6a2d.png

因此在這以前,須要先在一塊內存佈局好vtable,原文推薦使用「unity.window.contents.chunk」 命令,這個RPC命令會把咱們的參數複製進去data段上一個堆指針內部。

這個全局變量指針由命令「unity.window.contents.start」 建立。

這兩個unity的命令。有反序列化操做並且沒有官方文檔能夠看,只能本身慢慢debug,摸索出對應的結構。。具體的結構請看文章末尾的Github代碼。

call以後,首先須要一個stack pivot到堆上,而後就是愉快的ROP。

須要說明的是,vmware中的data段竟然是rwx的。。直接複製shellcode上去就能執行了。(用!address查看地址熟悉,內存佈局

http://p9.qhimg.com/t01d1109d08dff01e3a.png

具體的ROP見文章末尾的Github代碼。

----------------------------------------------------------

斷點參考:因爲沒法單步跟蹤,只有經過r,kb指令打印信息。

ba r8 vmware_vmx+0xb87100 ".echo chunk_addr;r;kb;g"//chunk地址,全局固定偏移
bp vmware_vmx+0x11b2d ".echo stack_piovt;kb;g"//棧遷移
bp vmware_vmx+0x69220 ".printf \"msg:%ma\\n\",@r8;dc r8 L10;.echo;g"//vmware_vmx接收的rpci命令
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x44fb40 ".echo appendbuf;kb;g"//appendbuf:my_DnD_TransportBufAppendPacket_4F0540,包拼接
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x44fbdb ".echo datacopy;r r8;dc rdx;dc rcx;kb;g"//call appendbuf->memcpy;+4F05DB,包拷貝狀況
ba r8 04d2c670 ".echo datacopynow;kb;gu;dq 04d2c670;g"//開始數據拷貝,04d2c670是dnd對象。
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x45e00b ".echo call0;r rbx;kb;g"//分析調用rop前,vtable如何跳轉
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x45e01c ".echo call1;r;kb;g"//vtable調用

調用的函數過程(數據拷貝)

1.vmware_vmx接收rpci命令

2.my_rpci_command_check_69220對rpci進行檢查,是不是支持的命令

3.sub_9D5F0?

4.my_finish_DnD_TransportBufAppendPacket_4FE960//函數5結束後,調用

.text:00000004FEA0B mov rcx, [rbx+8] ; r rbx
.text:00000004FEA0F mov r8, [rsp+28h+Memory]
.text:00000004FEA14 mov r9, rax
.text:00000004FEA17 mov r10, [rcx] ; r rcx
.text:00000004FEA1A xor edx, edx
.text:00000004FEA1C call qword ptr [r10+10h] ; 這裏發生虛函數調用。調用僞造的虛表。

   dnd+0x38存儲chunk的地址,全局變量:base+0xb87118;->chunk內容:0460f1f0;->Stackpiovt_Gadget:base+0x11b2d(poi(0460f1f0+10))

5.my_DnD_TransportBufAppendPacket_4F0540

6.memcpy//dnd.transport傳輸的包,拷貝。

----------------------------------------------------------

0x06 CopyPaste Object 覆蓋


若是覆蓋的是CP Object,那麼覆蓋掉vtable以後,vmx進程不會崩潰,原文推薦使用cp命令觸發vtable調用,而我用了這個Object的destructor。也就是再把版本設 置回4的話,程序會調用vtable中對應的destructor. 

經過上面提到的」unity.window.contents.start「命令能夠設置一個qword大小的gadget在程序的數據段上,而以前已經經過leak獲得了程序的基地址,所 以能夠獲得這個gadget的指針的地址。

這個點不是特別好用,寄存器的值不是很方便,但最終依然找到了合適的gadget來利用。詳細ROP見文章末尾Github 代碼。

-------------------

我的調試附圖:cp destructor調用過程

須要設置的斷點

ba r8 03c4cf50 ".echo access_cp;r;kb;g"//在訪問cp對象時觸發。cp地址能夠用s -a 0 L8000000 "exploit test"後肯定。
bp vmware_vmx+0x33700 ".echo getchunk_stackpiovt;dq 0399b090 l40;r;kb;g"//棧遷移命令。已經調用cp虛表,進入rop.
ba r8 vmware_vmx+0xb87100 ".echo accesschunkaddr;"//訪問chunkaddr,固定的全局地址。
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x437120 ".echo ropnow;r;kb;g"//rop地址
bp vmware_vmx+0x69220 ".printf \"msg:%ma\\n\",@r8;dc r8;g"//vmware_vmx接收的全部rpci消息。
bp vmware_vmx!opus_repacketizer_get_nb_frames+0x44fbdb ".echo datacopy;dc rdx;dc rcx;r;kb;g"//my_TransportBufAppendPacket_4F0540,進行數據拷貝,發生溢出的地方。

-------------------

0x07 最後說兩句


這個漏洞能不能穩定利用,關鍵在於堆佈局作的怎麼樣,這個方面我研究很少。。之後還得繼續看。長亭在這種狀況能達到60-80%的成功率,太厲害了。 

該漏洞在VMware Workstation 12.5.5以後被修補。

若是文章中有任何錯誤請在評論指出,謝謝各位表哥。

完整EXP:點我

相關文章
相關標籤/搜索