下文取自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
在正常操做系統中直接執行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中更加明顯:
----------------------------------------------------------------------------------
我的分析狀況:
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()
bindfun只是把對應的參數值寫入了全局變量,實際上是一個表。bindfun參數4就是對應rpc命令的處理函數,而rpc命令函數的參數3和參數4分別是咱們發送的RPC原始request和RPCrequest的長度。參數5和參數6是咱們獲得的 reply的地址和reply的長度。
能夠看出這個命令有一個參數,也就是版本號。
其餘的RPC命令相似,在發送「vmx.capability.dnd_version」命令的時候,對應的處理函數中若是發現當前版本和設置的版本不一致,就會調用函數建立新的 object,把原來的版本的object銷燬。
my_vmx_capability_dnd_83EE0
DnD和CP的Object的size都是同樣的,都是0xa8大小。vmware_vmx+9C470
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成功率仍是比較低。
思路大概就是把內存變成這個樣子:
若是一旦沒有佈局成功。。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)會馬上發生調用。
因此在這以前,須要先在一塊內存佈局好vtable,原文推薦使用「unity.window.contents.chunk」 命令,這個RPC命令會把咱們的參數複製進去data段上一個堆指針內部。
這個全局變量指針由命令「unity.window.contents.start」 建立。
這兩個unity的命令。有反序列化操做並且沒有官方文檔能夠看,只能本身慢慢debug,摸索出對應的結構。。具體的結構請看文章末尾的Github代碼。
call以後,首先須要一個stack pivot到堆上,而後就是愉快的ROP。
須要說明的是,vmware中的data段竟然是rwx的。。直接複製shellcode上去就能執行了。(用!address查看地址熟悉,內存佈局)
具體的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:點我。