前言:
這個項目是我在2017年和黎同學申請的南京郵電大學STITP(大學生創新訓練計划)項目。
在將近一年的緊湊時間內,我和黎同學在老師以及學長的幫助下,從立項開始一點點地開始學習如何復現這個當時還十分火熱的CVE。
由於當時還沒有系統學習網安專業知識,研究時間也是在課余時間里擠出來的,因此會有一些倉促,但好在有了老師和學長的指導,最終成功進行了復現並提出了一些改進方法。
由於已經難以找到當時的報告以及部分研究調試數據,為此我根據最終答辯時的PPT制作了這個核心思路總結概述。
總結的過程有一些簡陋,如果有所缺漏請指出,謝謝。
正文:
1、 后門(BACKDOOR)接口
這個接口使得在用戶態就可以通過這個接口發送命令和host通信函數中含有一個”inl”指令會導致用戶態程序產生權限錯誤,該錯誤會被host的hypervisor捕捉從而實現通信,類似於一個錯誤中斷。
void Backdoor_InOut(Backdoor_proto *myBp) // IN/OUT { uint64 dummy; __asm__ __volatile__( #ifdef __APPLE__ /* * Save %rbx on the stack because the Mac OS GCC doesn't want us to * clobber it - it erroneously thinks %rbx is the PIC register. * (Radar bug 7304232) */ "pushq %%rbx" "\n\t" #endif "pushq %%rax" "\n\t" "movq 40(%%rax), %%rdi" "\n\t" "movq 32(%%rax), %%rsi" "\n\t" "movq 24(%%rax), %%rdx" "\n\t" "movq 16(%%rax), %%rcx" "\n\t" "movq 8(%%rax), %%rbx" "\n\t" "movq (%%rax), %%rax" "\n\t" "inl %%dx, %%eax" "\n\t" /* NB: There is no inq instruction */ "xchgq %%rax, (%%rsp)" "\n\t" "movq %%rdi, 40(%%rax)" "\n\t" "movq %%rsi, 32(%%rax)" "\n\t" "movq %%rdx, 24(%%rax)" "\n\t" "movq %%rcx, 16(%%rax)" "\n\t" "movq %%rbx, 8(%%rax)" "\n\t" "popq (%%rax)" "\n\t" #ifdef __APPLE__ "popq %%rbx" "\n\t" #endif : "=a" (dummy) : "0" (myBp) /* * vmware can modify the whole VM state without the compiler knowing * it. So far it does not modify EFLAGS. --hpreg */ : #ifndef __APPLE__ /* %rbx is unchanged at the end of the function on Mac OS. */ "rbx", #endif "rcx", "rdx", "rsi", "rdi", "memory" ); }
2、 RPCI(Remote Procedure Call Interface)
基於Backdoor機制實現,通過這個機制,guest可以向host發送請求完成某些操作,例如拖放(Drag n Drop)和復制黏貼(Copy Paste),RPCI依據info-get guestinfo.ip獲取guest的ip地址,這是通過套接字實現的。
3、 漏洞
類似於過去在Version4中所出現的漏洞,在Version3中也同樣存在,當guest發送DnD/CP數據包時,host會重組guest發送的DnD/CP消息。關鍵問題在於,函數只檢查了包頭的buffer長度,對后續的數據包檢查無效,因此可以在后續的包中指定更大的binarySize來滿足檢查,觸發溢出。
4、 漏洞利用執行方法
我們需要在堆上覆蓋一個函數指針,或者破壞C++的虛表指針
(1)首先將DnD/CP協議設置為version 3,RPCI在檢查到DnD/CP協議版本修改時會創建一個對應版本的C++對象,對於version3,兩個C++對象會被創建,一個用於DnD,一個用於CP
tools.capability.dnd_version 3 tools.capability.copypaste_version 3 vmx.capability.dnd_version vmx.capability.copypaste_version
(2)分配一個內存塊,讓它分配在C++對象前,利用堆溢出改寫C++對象的vtable指針(虛表),讓其指向可控內存,執行SHELL
5、 繞過ASLR
在實現上述過程首先要繞過保護機制ASLR(地址空間布局隨機化),關鍵在於找到能夠破壞的,帶有長度或數據指針的對象,並且可以被guest讀取。於是找到了“info-set”和”info-get”
info-set guestinfo.KEY VALUE info-get guestinfo.KEY
我們可以通過溢出來覆蓋結尾的null字節,讓字符串連接上相鄰的內存塊。如果我們能夠在發生溢出的內存塊和DnD或CP對象之間分配一個字符串,那么我們就能泄露對象的vtable地址,從而我們就可以知道vmware-vmx的地址。
問題在於,這種覆蓋可能導致關鍵函數被覆蓋從而崩潰(卡到母系統有時候都一卡一卡的,每次運行都是一種折磨,虛擬機甚至有時候都要重裝)
具體實現策略:
1.首先分配一些填滿“A”的字符串,
2.然后通過溢出寫入一些“B”,
3.接下來讀取所有分配的字符串,其中含有“B”的就是被溢出的字符串。
4.這樣我們就找到了一個字符串可以被用來讀取泄露的數據,然后以bucket的內存塊大小0xA8的粒度繼續溢出,每次溢出后都檢查泄露的數據。
5.由於DnD和CP對象的vtable距離vmware-vmx基地址的偏移是固定的,每次溢出后只需要檢查最低一些數據位,就能夠判斷溢出是否到達了目標對象。
這個是原作者的圖,我覺得十分形象所以拿過來解釋一下
執行流程圖
6、 兩種情形,兩種方法
(1)DND
不能只覆蓋vtable指針,需要偽造一個vtable來躲避訪問
(2)CP
覆蓋虛指針,讓它指向我們可控的其他數據,這些數據可以用作對象虛表,只要我們找到一個指向可控數據的指針就可以解決
7、 如何解決LFH堆問題
LFH堆一旦開啟便無法關閉,但是使用一些全局標志可以導致無法啟用LFH(gflags調試)
按照微軟百科的描述,當gflags中某些調試項出現時會導致LFH堆無法正常開啟
我們當時是找到了一些和pageheap有關的調試選項可以導致上述現象的發生,但是具體參數我們來不及找出來了(這還是我們后期找出來的)
但我們並不確定這些“理想條件”是否能直接利用,因為畢竟它是在調試工具中,並不在代碼中實現,時至今日我們也只是有一些皮毛理解。
所以我們當初給出的是一個解決方案,而不是一個具體的代碼實現。
8、 例圖
由於我們之前執行獲得的結果截圖沒有保存,因此這里使用的是原作者的例圖。
我們使用的是WINXP虛擬系統實現的逃逸。