虛擬機逃逸漏洞(CVE-2017-4901)的研究以及復現


前言:

      這個項目是我在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虛擬系統實現的逃逸。

 

 

參考來源(原作者):https://zhuanlan.zhihu.com/p/27733895?utm_medium=social&utm_source=wechat_timeline&from=timeline&isappinstalled=1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM