<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    【技術分享】偽造面向對象編程——COOP

    一顆小胡椒2022-04-21 09:08:11

    C和C++向來以“let the programmer do what he wants to do”的貼近底層而為廣大開發者所喜愛。語言對開發者行為的較少限制,就使其成為不安全的語言。針對C和C++程序的控制流劫持攻擊,如ROP、JOP等已經在長期的實踐中證明了其破壞力,而眾多保護措施也已經被提出。攻擊者要么是將控制流轉向其注入的惡意代碼,要么是借由代碼重用攻擊來惡意地重用進程空間中已有的代碼片段。無論是已有的攻擊,還是相應的防御措施,往往都沒有,或者是很少考慮C++的自身語言特性,包括其面向對象的特性。而現如今許許多多的應用程序都是通過C++開發,或者是包含部分C++代碼,如Microsoft Internet Explorer,Google Chrome, Mozilla Firefox, Adobe Reader, Microsoft Office, LibreOffice, 和openJDK等等。因此,針對C++語言特性的攻擊很可能造成巨大的破壞。接下來,我就為大家介紹一下針對C++特性的攻擊——偽造面向對象編程(COOP)。

    偽造面向編程(counterfeit object-oriented programming ,以下簡稱COOP)是由Felix Schuster等人于2015年提出來的一種主要針對C++語言特性的攻擊方式。C++提供了面向對象的特性,如類、方法以及虛函數等。而COOP就利用了C++程序中的虛函數都要進行取地址操作(因為要維持一張虛函數表),而這就意味著有一個存在一個不變的指針來對應一個虛函數,同時也會使得C++程序中相比同樣體量的C程序中存在更多的進行取地址操作的函數。下面我具體給大家介紹一下。

    前期知識

    二進制層面的C++虛函數

    對于每個至少包含一個虛函數的對象來說,在其內部偏移0處都會存在一個相應的指針,通常被稱為vptr。如下圖所示:

    可以看到,類A不含虛函數,那么它的內存布局中就沒有vptr指針和虛函數表。類B則恰好相反,在其內部偏移0處存放有vptr指針,指向虛函數表。

    而調用一個虛函數通常會對應類似以下的匯編指令:

    rcx寄存器存儲著this指針,通過取內容將vptr指針存入rax寄存器,然后再通過相應的偏移量(此處是8,但有可能是其他數)讀取虛函數表,取出對應虛函數地址進行調用。

    攻擊假設

    COOP對攻擊者的能力做了如下假設:

    1. 攻擊者控制了一個包含虛函數的C++對象.
    2. 攻擊者能夠推斷出一個他已經知道內存布局(至少是部分知道)的C++模塊基址。

    對于第一點來說,攻擊者只要利用鄰近該對象的某處溢出漏洞或者是use-after-free漏洞(正如后續某些攻擊時所提到的那樣)。

    而對于第二點來說,一個公開的C++庫就能符合要求。

    攻擊目標

    COOP的設計初衷是為了達成以下目標:

    (1)不出現已有代碼重用攻擊的特征,包括:

    1. 不會間接跳轉(通過call 或者是jmp指令)到沒有被取地址(被取地址的通常包括函數頭等)的內存位置。
    2. 不會不經調用棧(call stack)執行return操作。
    3. 控制流中不出現過多的間接跳轉。
    4. 不會劫持棧上指針。
    5. 不會注入新的或者是利用已有的代碼指針(返回地址或者是函數指針)。

    (2)使控制流和數據流盡量與一般的C++代碼相似。

    (3)能被廣泛地用于攻擊C++程序。

    (4)在真實情景下實現圖靈完備(Turing complet,具體定義比較復雜,可以自行了解,簡單的說就是能夠實現條件分支,循環、讀寫等操作)。

    攻擊實施步驟

    為了更好地理解COOP攻擊模式,在介紹COOP具體攻擊實施步驟時,將會使用簡單的代碼來進行介紹,主要涉及以下幾個類:Student、Course和Exam。

    (1)劫持C++對象

    每一次COOP攻擊都要以劫持目標C++對象開始,稱為initial object。這一步是為了是控制流轉向下一步偽造的對象當中。比如可以劫持以下類Course:

    students是一個指向數組的指針。其中的Student類定義如下:

    (2)偽造對象

    在上一步操作之后已經可以使控制流發生改變,接下來要做的就是偽造包含有攻擊者選定的vptr指針和一些數據域的對象。偽造的對象并不是目標程序自身有的,而是由攻擊者注入到程序進程空間中的。偽造的對象和必要的一些數據將會作為連續的一個內存塊(chunk)被注入到攻擊者控制的某片內存區域。注入偽造對象后的內存布局如下圖所示:

    其中的object0和object1就是偽造的對象,而initial object(也就是上文提到的Course類)已經被劫持,students數組各成員已經指向相應的偽造對象,nStudents已經被設置為偽造對象的數量。接下來要做的就是通過各個vptr指針調用攻擊者選定的虛函數了。

    (3)調用虛函數

    通過initial object和偽造對象,相應的vptr指針和需要的數據已經準備好,接下來就是調用攻擊者選定的虛函數執行相應操作了。值得注意的是,由于COOP的目標之一是使控制流和數據流盡量與一般的C++代碼相似,所以各個vptr指針應該指向實際存在的虛函數表(理想情況下應該是虛函數表開頭)。與ROP中的gadgets類似,COOP中的目標虛函數被稱為vfgadgets,從其功能上來看,具體可以分為以下幾類:

    1. 主循環函數ML-G。

    ML-G是包含有以指向偽造對象指針為循環變量的循環的虛函數,比如前述Course類中的?Course函數:

    該函數會在循環過程中訪問每個students數組成員,而該數組已經指向偽造對象,也就是說,通過該循環能將控制流轉向偽造對象。

    現實當中也有相應的例子,比如VS 2013 agents.h中:

    1. 算數或邏輯運算函數ARITH-G。

    如Exam類中以下虛函數:

    1. 內存讀/寫函數W-G/R-G。

    如涉及字符串讀寫等函數。如以下函數:

    1. 調用函數指針函數INV-G。

    如以下函數:

    1. 條件寫函數W-COND-G。
    2. 帶有initial object的數據作為參數的主循環函數ML-ARG-G。

    1. 寫入第一個參數指針指向地址的函數W-SA-G。

    1. 只調整棧指針而無其他操作的函數MOVE-SP-G。
    2. 64位下讀取參數寄存器rdx,r8,r9函數LOAD-R64-G。

    有了這些類型的vfgadgets,COOP的整個流程就比較清晰了:從initial object的vptr指針開始,調用第一個虛函數進入到主循環函數ML-G(如果選定的主循環函數有參數的話就是ML-ARG-G),然后循環地通過各個偽造對象的vptr指針去調用對應的由攻擊者選定的虛函數。整個流程如下圖所示:

    其中各個數字代表執行順序。而為了實現不同的操作,就需要以上幾類vfgadgets的協同,這就是接下來要介紹的。

    (4)不同操作的實現

    正如前述所討論的那樣,通過主循環函數ML-G ,任意數目的虛函數可以被執行。而通過不同vfgadgets的配合,可以實現以下操作:

    1. 任意寫。

    考慮如下Examl類:

    其各個元素在內存中排布如下:

    可以看到,SimpleString::set(object1)的buffer指針和Exam(object0)的score是重疊的。而buffer指針是字符串復制的目標地址,在此基礎上,通過操縱score的值,實際上buffer的值也就能夠控制,進而任意寫的地址任意性就達成了,接下來就要解決源數據(此處的字符串s)。而注意到此處的字符串是傳入的參數,那么接下來要解決的就是傳參問題了。以上在內存中構造重疊的對象是COOP很常用的手法。

    1. 傳遞參數。

    參數傳遞和具體的系統有關,主要有:

    (A)windows x64

    windows x64平臺下函數的前四個參數由rcx,rdx,r8和r9四個寄存器來傳遞,更多的參數則通過棧來傳遞。特別的,對于C++程序來說,this指針通過rcx寄存器來傳遞。其他三個寄存器通常會作為臨時寄存器使用。考慮下列64位下讀取參數寄存器vfgadget:

    其對應匯編指令如下:

    相當于進行了如下操作:

    因此,只要攻擊者合理地選取對應偏移量為10h和18h的數據,就能對應的改變相應寄存器的值,進而通過這些寄存器為某個目標函數傳參。

    (B)Linux x64

    Linux x64使用了6個寄存器來傳遞前六個參數,利用原理與windows x64是相同的,但因為有更多寄存器,所以更加簡單。

    (C)windows x86

    windows x86下,this指針通過ecx寄存器傳遞,其他參數通過棧來傳遞。這個時候就要以下列帶參數的主循環函數ML-ARG-G來傳遞參數。

    其對應匯編指令為:

    其中第9-22行進行了如下操作:

    使用下列寫入第一個對參數指針指向地址的vfgadget,就可以實現對以上arg0的控制,進而執行傳參操作。

    或者也可以借由內存寫vfgadget來進行。

    (D)Linux x86

    該平臺下的傳參均通過棧來進行,所以使用帶參數的主循環函數ML-ARG-G來傳遞參數。

    以上均是只同時傳遞一個參數,要想同時傳遞多個參數,就有必要做棧平衡。下圖展示了不同參數個數的棧情形:

    棧平衡操作可以使用MOVE-SP-G類型的vfgadget來完成,最終達到的效果就是參數被逐個堆積到棧上,如下圖所示:

    1. 調用API函數

    要調用API函數,攻擊者可以直接將偽造對象的vptr指針指向已加載模塊的IAT表或者是EAT表,這二者均存有API函數信息。如果遇到權限問題,則可以用this指針作為參數調用VirtualProtect()函數設置即可。

    又或者攻擊者可以使用調用函數指針的vfgadget——INV-G來調用API函數。

    1. 實現條件分支和循環

    如果選定的變量沒有存儲在棧上,那么下列條件寫vfgadget——W-COND-G就能實現條件分支。

    如果變量在棧上,MOVE-SP-G類型的vfgadget也可以被用作分支。

    循環的實現也是類似的。

    工具框架

    為了完成COOP攻擊,就要完成以下兩個任務:

    1. 尋找目標vfgadgets

    為了識別應用程序中有用的vfgadget,搜索時僅依賴于二進制代碼以及可選的調試符號。使用IDA將目標C++模塊反匯編,C ++模塊中的每個虛函數都被視為潛在的vfgadget。使用調試符號來靜態標識C ++模塊中的所有虛函數表。如果沒有該符號以標識虛函數表,就使用啟發式的方法:把所有帶有地址信息的函數指針數組作為一個潛在的虛函數表。檢查所有已識別的虛函數,通常只將具有一個或三個基本塊的虛函數作為潛在vfgadget。唯一的例外是ML-G和ML-ARG-G,由于帶有循環,它們通常由更多基本塊組成。

    搜索時,每一個基本塊用靜態單賦值形式(static single assignment form,通常簡寫為SSA form或是SSA,是中間表示IR的特性,每個變數僅被賦值一次)進行總結以反映其I/O行為特征。依賴于METASM二進制代碼分析工具包的backtracking功能,在基本塊級別進行符號執行(symbolic execution)操作。接下來就是對潛在vfgadgets基本塊的SSA表示應用過濾器。例如 “賦值的左側不能引用任何參數寄存器;右側不能引用this指針”能識別64位系統下的內存寫vfgadget——W-G。

    1. 構造重疊的偽造對象

    正如前述所說的,構造重疊的偽造對象對于COOP很重要。具體實現時,攻擊者定義偽造的對象和標簽。可以將標簽分配給偽造對象內的任何字節。當為不同對象中的字節分配相同的標簽時,應確保將這些字節映射到最終緩沖區中的相同位置,同時確保具有不同標簽的字節被映射到不同的位置。這些約束通常可以滿足,因為偽造對象中的實際數據通常很少。

    比如,偽造的對象A僅僅在內部偏移+0處有vptr指針,偏移+16處有個整數,偏移+136處有標簽X。偽造對象B只有vptr指針,內部偏移+8處有相同標簽X。那么,B+8就可以映射到A+136處。 

    實際攻擊

    COOP進行了以下幾個POC:

    1.64位IE 10

    攻擊64位IE 10使用了21個偽造對象,其中8個是重疊的。另外使用了mshtml.dll中8個不同的vfgadgets。攻擊有兩條執行路徑:a.彈出計算器和b.打開畫圖。以下是攻擊使用的vfgadgets:

    2.32位IE 10

    攻擊32位IE 10使用了22個偽造對象,其中6個是重疊的。另外使用了mshtml.dll、ieframe.dll、Jscript.dll等三個動態鏈接庫中11個不同的vfgadgets。攻擊調用了WinExec()彈出了計算器。以下是攻擊使用的vfgadgets:

    3.64位 Firefox 36.0a1

    攻擊使用了9個偽造對象,其中2個是重疊的。另外使用了libxul.so中5個不同的vfgadgets。攻擊執行了system(“/bin/sh”)。以下是攻擊使用的vfgadgets:

    4.其他人進行的COOP攻擊

    鏈接為翻譯,原文在鏈接都有相應鏈接。

    使用最新的代碼重用攻擊繞過執行流保護(https://bbs.pediy.com/thread-217335.htm)

    防御COOP

    由于COOP主要針對C++語言特性,所以較好考慮了C++語言特性的防御措施都能比較好的地防御  COOP攻擊。COOP提出者在后續論文里也提出了表隨機化(Table Randomization)來對抗COOP。

    指針虛函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    UAF漏洞全稱為use after free,即釋放后重用。
    C和C++向來以“let the programmer do what he wants to do”的貼近底層而為廣大開發者所喜愛。
    逆向角度看C++部分特性
    軟件漏洞分析簡述
    2022-07-18 07:08:06
    然后電腦壞了,借了一臺win11的,湊合著用吧。第一處我們直接看一下他寫的waf. 邏輯比較簡單,利用正則,所有通過 GET 傳參得到的參數經過verify_str函數調用inject_check_sql函數進行參數檢查過濾,如果匹配黑名單,就退出。但是又有test_input函數進行限制。可以看到$web_urls會被放入數據庫語句執行,由于$web_urls獲取沒有經過過濾函數,所以可以
    控制流劫持攻擊是當前較為主流的攻擊方式之一,包括ROP、JOP等等。
    House of Cat5月份偶然發現的一種新型GLIBC中IO利用思路,目前適用于任何版本,命名為House of cat并出在2022強網杯中。但是需要攻擊位于TLS的_pointer_chk_guard,并且遠程可能需要爆破TLS偏移。并且house of cat在FSOP的情況下也是可行的,只修改虛表指針的偏移來調用_IO_wfile_seekoff即可。vtable檢查在glibc2.24以后加入了對函數的檢測,在調用函數之前首先會檢查函數地址的合法性。
    幾乎所有Win32程序都會加載ntdll.dll和kernel32.dll這兩個基礎的動態鏈接庫。64位系統首先通過選擇字GS在內存中找到當前存放著指向當前線程環境塊TEB。進程環境塊中偏移位置為0x18的地方存放著指向PEB_LDR_DATA結構體的指針,其中,存放著已經被進程裝載的動態鏈接庫的信息。模塊初始化鏈表 InInitializationOrderModuleList中按順序存放著 PE 裝入運行時初始化模塊的信息,第一個鏈表結點是 ntdll.dll,第二個鏈表結點就是 kernel32.dll。從kernel32.dll的加載基址算起,偏移0x3C的地方就是其PE頭。
    EXP編寫學習之繞過GS
    2023-02-20 09:58:16
    棧中的守護天使 :GSGS原理向棧內壓入一個隨機的DWORD值,這個隨機數被稱為canary ,IDA稱為 Security Cookie。Security Cookie 放入 ebp前,并且data節中存放一個 Security Cookie的副本。棧中發生溢出時,Security Cookie首先被淹沒,之后才是ebp和返回地址。函數返回之前,會添加一個Security Cookie驗證操作,稱為Security Check。檢測到溢出時,系統將進入異常處理流程,函數不會正常返回,ret也不會被執行。函數使用無保護的關鍵字標記。緩沖區不是8字節類型 且 大小不大于4個字節。可以為函數強制啟用GS。
    源碼分析1、LLVM編譯器簡介LLVM 命名最早源自于底層虛擬機的縮寫,由于命名帶來的混亂,LLVM就是該項目的全稱。LLVM 核心庫提供了與編譯器相關的支持,可以作為多種語言編譯器的后臺來使用。自那時以來,已經成長為LLVM的主干項目,由不同的子項目組成,其中許多是正在生產中使用的各種 商業和開源的項目,以及被廣泛用于學術研究。
    什么是UAF漏洞?當訪問指向已釋放對象的指針時,就會發生UAF漏洞。它沒有任何意義!為什么程序員要釋放一個對象,然后再次訪問它?
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类