欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]CVE-2018-8550分析与复现

[原创]CVE-2018-8550分析与复现

这个漏洞属于com组件Unmarshal类型本地权限提升漏洞

复现环境

  1. Windows 10 1709 32位操作系统
  2. 需要安装声卡或操作系统自带虚拟声卡
  3. 编译环境Visual Studio 2013

Poc 分析

原poc作者James Forshaw使用C#实现,我一直未复现成功,不过通过原poc的代码我大致明白了漏洞的成因和触发方法,原poc环境是win10 1803 X64系统.在cve2018-8550更新补丁出来不久,微软就取消了64位ole32.dll和coml2.dll调试符号提供,不过32的仍然可以正常提供.为了方便调试我用vc在32位系统上成功复现了poc.
poc采用的反射从低权限进程(poc进程)向高权限进程(bits服务)Unmarshal方式实现触发bits复制了一个poc进程中的一个句柄至高权限进程从而用来创建进程实现提权,Unmarshal采用的ClassID为CLSID_DfMarshal={ 0x0000030B, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, } ,Bits服务器调用了ole32(在win10中被代码迁移到coml2)的DfUnMarshalInterface方法来Unmarshal的数据流中SDfMarshalPacket被poc精心构造的数据包,由于poc使用的是StgCreateDocfile创建IStorage对象,所以最终DfUnMarshalInterface调用了CExposedDocFile::Unmarshal来Unmarshal,下面是逆向的代码:

int __stdcall CExposedDocFile::Unmarshal(IStream *pstm, void **ppv, unsigned int mshlflags)

{

  int hrFinal; // esi

  int hrTempNext; // eax

  CPubDocFile *CPubDocFileObj; // eax

  CGlobalContext *CGlobalContextObj; // eax

  const void *v7; // eax

  CGlobalContext *CGlobalContextObjRef; // eax

  CPubDocFile *CBasedPubDocFileObjFirst; // eax

  unsigned int fUnmarshalOriginalConfig; // eax

  CPubDocFile *CBasedPubDocFileObjFirstRef; // ecx

  int fUnmarshalOriginalConfigRef; // ST10_4

  int IsRootConfig; // eax

  void *ReservedForOleRef; // ebx

  unsigned int CMarshalListObj_baseObj; // ecx

  unsigned int pid; // eax

  CMarshalList *CMarshalListObjFirst; // ecx

  CMarshalList *CMarshalListObjFound; // eax

  CExposedDocFile *CExposedDocFile_MarshalList_Final; // esi

  CExposedDocFile *CExposedDocFileObjNext; // eax MAPDST

  CDFBasis *CBasedDFBasisObj; // esi

  CPubDocFile *CBasedPubDocFileObjNext; // edx

  CSmAllocator *CSmAllocator_A; // eax

  CSmAllocator *CSmAllocator_B; // eax

  unsigned int CBasedMarshalListObj; // eax

  CMarshalList *CBasedMarshalListObjNext; // ecx

  CPubDocFile *CPubDocFileObjNext; // eax

  CDFBasis *BasedDFBasisObj; // eax

  CPerContext *CPerContextObjRef; // ecx

  CSmAllocator *CSmAllocator_C; // eax

  CSmAllocator *CSmAllocator_D; // eax

  CPerContext pcSharedMemory; // [esp+Ch] [ebp-8Ch]

  unsigned int cbRead; // [esp+50h] [ebp-48h]

  SDfMarshalPacket SDfMarshalPacketCurrent; // [esp+54h] [ebp-44h]

  IStorage *pstorgeStd; // [esp+88h] [ebp-10h]

  CDfMutex mtx; // [esp+8Ch] [ebp-Ch]

  CPerContext *CPerContextObj; // [esp+94h] [ebp-4h]

  void *pvBaseOld; // [esp+A0h] [ebp+8h]

  // SDfMarshalPacketCurrent初始化里面的字段指针都是nullptr

  mtx._pGlobalPortion = 0;

  mtx._hLockEvent = 0;

  pstorgeStd = 0;

  SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedPubStreamObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedSeekPointerObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedDFBasisObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedGlobalFileStreamObj._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedGlobalFileStreamDirty._SelftobjectPtr = 0;

  SDfMarshalPacketCurrent.CBasedGlobalFileStreamOriginal._SelftobjectPtr = 0;

  // 构造pcSharedMemory是一个cpercontext,先构造变量

  CPerContext::CPerContext(&pcSharedMemory, 0);

  // 通用marshal方法先CoUnmarshalInterface

  CoUnmarshalInterface(pstm, &IID_IStorage, (IUnknown *)&pstorgeStd);

  if ( (mshlflags & 0x80000000) != 0 )

  {

    // 这里都读流int就是largeint

    hrFinal = ((int (__stdcall *)(IStream *, signed int, _DWORD, signed int, _DWORD))pstm->_SelfStreamVtbl->Seek)(

                pstm,

                96,

                0,

                1,

                0);

    if ( hrFinal >= 0 )

      hrFinal = -2147287039;

    goto EH_std_0;

  }

  // 把SDfMarshalPacketCurrent读出来

  hrFinal = pstm->_SelfStreamVtbl->Read(pstm, &SDfMarshalPacketCurrent, 52u, &cbRead);

  if ( hrFinal >= 0 )

  {

    if ( cbRead != 52 )

    {

      hrFinal = -2147287010;

      goto EH_std_0;

    }

    // pcSharedMemory也是从packet中反序列化出来的,里面需要判断进程id

    UnmarshalSharedMemory(&SDfMarshalPacketCurrent, mshlflags, &pcSharedMemory);

    hrFinal = hrTempNext;

    if ( hrTempNext < 0 )

      goto EH_std_0;

    // 为ReservedForOle赋值一个局部变量

    pvBaseOld = *(void **)NtCurrentTeb()->ReservedForOle;

    // 反序列化出来全局baseDocFile,需要先验证这个是否正确

    if ( SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr )

      CPubDocFileObj = (CPubDocFile *)(SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr

                                     + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

    else

      CPubDocFileObj = 0;

    // 验证只是判断里面的sig

    hrFinal = CPubDocFile::Validate(CPubDocFileObj);

    if ( hrFinal < 0 )

    {

EH_Err_109:

      CPerContext::SetThreadAllocatorState(&pcSharedMemory, 0);

      CSmAllocator_A = GetTlsSmAllocator();

      CSmAllocator::Uninit(CSmAllocator_A);

      CSmAllocator_B = GetTlsSmAllocator();

      CSmAllocator::SetState(CSmAllocator_B, 0, 0, 0, 0, 0);

      goto EH_std_0;

    }

    if ( SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr )

      CGlobalContextObj = (CGlobalContext *)(SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr

                                           + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

    else

      CGlobalContextObj = 0;

    if ( !CGlobalContextObj

      || (!SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr ? (v7 = 0) : (v7 = (const void *)(SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr + *(_DWORD *)NtCurrentTeb()->ReservedForOle)),

          !IsValidPtrIn(v7, 0x30u)) )

    {

      hrFinal = -2147287031;

      goto EH_Err_109;

    }

    // 全局环境变量CGlobalContextObjRef也反序列出来

    if ( SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr )

      CGlobalContextObjRef = (CGlobalContext *)(SDfMarshalPacketCurrent.CBasedGlobalContextObj._SelftobjectPtr

                                              + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

    else

      CGlobalContextObjRef = 0;

    // 里面是CGlobalContext的初始化它的锁createevent

    hrFinal = CDfMutex::Init(&mtx, CGlobalContextObjRef, 0);

    if ( hrFinal < 0 )

      goto EH_Err_109;

    // 锁验证一下

    hrFinal = CDfMutex::Take(&mtx, 0xFFFFFFFF);

    if ( hrFinal < 0 )

      goto EH_Err_109;

    // 反序列化出来一个临时的PubDocFileObj和它的ref同类型局部变量不牵涉下下面具体分配

    if ( SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr )

      CBasedPubDocFileObjFirst = (CPubDocFile *)(SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr

                                               + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

    else

      CBasedPubDocFileObjFirst = 0;

    fUnmarshalOriginalConfig = CBasedPubDocFileObjFirst->PRevertableObj._UnmarshalOriginalConfig;

    if ( SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr )

      CBasedPubDocFileObjFirstRef = (CPubDocFile *)(SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr

                                                  + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

    else

      CBasedPubDocFileObjFirstRef = 0;

    // 2个配置做与运算双重判断

    fUnmarshalOriginalConfigRef = fUnmarshalOriginalConfig & 4;

    // 这个配置判断是否是Rootl类型的PubDocFile

    IsRootConfig = CPubDocFile::IsRoot(CBasedPubDocFileObjFirstRef);

    // 这里只是根据读取到的2个配置反序列化出来一个环境变量CPerContextObj

    hrFinal = UnmarshalContext(

                &SDfMarshalPacketCurrent,

                &CPerContextObj,

                mshlflags,

                // 是否Root配置

                IsRootConfig,

                // Unmarshal配置

                fUnmarshalOriginalConfigRef);

    if ( hrFinal < 0 )

    {

EH_mtx_0:

      CDfMutex::Release(&mtx);

      goto EH_Err_109;

    }

    // 保存ReservedForOle局部临时变量

    ReservedForOleRef = *(void **)NtCurrentTeb()->ReservedForOle;

    if ( gs_iSharedHeaps <= 256 )

    {

      // 把CMarshalList反序列化出来先验证下

      if ( SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr )

        CMarshalListObj_baseObj = SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr

                                + *(_DWORD *)NtCurrentTeb()->ReservedForOle;

      else

        CMarshalListObj_baseObj = 0;

      if ( CExposedDocFile::Validate((CExposedDocFile *)(CMarshalListObj_baseObj != 0 ? CMarshalListObj_baseObj - 72 : 0)) < 0 )

      {

        // 不通过最近指针就是nullptr

        CExposedDocFile_MarshalList_Final = 0;

      }

      else

      {

        // 获取当前的ProcessId,做判断,执行第一种逻辑

        pid = GetCurrentProcessId();

        // CMarshalListObj也是从packet中反序列化出来的

        if ( SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr )

          CMarshalListObjFirst = (CMarshalList *)(SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr

                                                + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

        else

          CMarshalListObjFirst = 0;

        // 如果是当前进程先从CMarshalList找到最终的CExposedDocFile

        CMarshalListObjFound = CMarshalList::FindMarshal(CMarshalListObjFirst, pid, ReservedForOleRef);

        // 找到CMarshalListObjFound后只是它的指针偏移量减6作为最终结果

        CExposedDocFile_MarshalList_Final = (CExposedDocFile *)(CMarshalListObjFound != 0 ? (unsigned int)&CMarshalListObjFound[-6] : 0);

      }

      if ( CExposedDocFile_MarshalList_Final )

      {

        // 如果找到找到最终的CExposedDocFile的FBasis就从之前的CPerContextObj把里面字段赋值

        if ( SDfMarshalPacketCurrent.CBasedDFBasisObj._SelftobjectPtr )

          // BasedDFBasisObj也是反序列出来之后再执行赋值

          BasedDFBasisObj = (CDFBasis *)(SDfMarshalPacketCurrent.CBasedDFBasisObj._SelftobjectPtr

                                       + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

        else

          BasedDFBasisObj = 0;

        CPerContextObjRef = CPerContextObj;

        // 赋值之后就完成unmarshal

        BasedDFBasisObj->_lockbytesBasePtr = CPerContextObj->_LockBytesBasePtr;

        BasedDFBasisObj->_CFileStreamDirtyPtr = CPerContextObjRef->_CFileStreamDirtyPtr;

        BasedDFBasisObj->_LockBytesOriginalPtr = CPerContextObjRef->_LockBytesOriginalPtr;

        CExposedDocFile_MarshalList_Final->baseIUnkownPtr._SelfMarshalVtbl->AddRef((IUnknown *)CExposedDocFile_MarshalList_Final);

        // 最终结果addref环境局部变量释放release

        CPerContext::Release(CPerContextObj);

      }

      else

      {

        // 如果从MarshalList未找到

        CExposedDocFileObjNext = (CExposedDocFile *)CMallocBased::operator new(0x8Cu, CPerContextObj->_pMalloc);

        // 初始化最终的CExposedDocFileObjNext

        if ( CExposedDocFileObjNext )

        {

          if ( SDfMarshalPacketCurrent.CBasedDFBasisObj._SelftobjectPtr )

            //  BasedDFBasisObj同样是反序列出来之后但是不赋值,可以为nullptr最为最终结果的构造函数参数传入

            CBasedDFBasisObj = (CDFBasis *)(SDfMarshalPacketCurrent.CBasedDFBasisObj._SelftobjectPtr

                                          + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

          else

            CBasedDFBasisObj = 0;

          if ( SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr )

            // PubDocFileObj同样是反序列出来之后但是不赋值,可以为nullptr最为最终结果的构造函数参数传入

            CBasedPubDocFileObjNext = (CPubDocFile *)(SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr

                                                    + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

          else

            CBasedPubDocFileObjNext = 0;

          // !!!构造CExposedDocFileObjNext,它才是最终结果!!!

          CExposedDocFile::CExposedDocFile(

            CExposedDocFileObjNext,

            CBasedPubDocFileObjNext,

            CBasedDFBasisObj,

            CPerContextObj);

          // 最终结果就是刚才构造的CExposedDocFileObjNext

          CExposedDocFile_MarshalList_Final = CExposedDocFileObjNext;

        }

        else

        {

          CExposedDocFile_MarshalList_Final = 0;

        }

        // 如果构造出来的CExposedDocFile_MarshalList_Final 失败

        if ( !CExposedDocFile_MarshalList_Final )

          goto LABEL_54;

        if ( SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr )

          CBasedMarshalListObj = SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr

                               + *(_DWORD *)NtCurrentTeb()->ReservedForOle;

        else

          CBasedMarshalListObj = 0;

        if ( CBasedMarshalListObj )

        {

          // 不是当前进程也需要反序列出来MarshalList把最终结果加进入

          if ( SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr )

            CBasedMarshalListObjNext = (CMarshalList *)(SDfMarshalPacketCurrent.CBasedMarshalListObj._SelftobjectPtr

                                                      + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

          else

            CBasedMarshalListObjNext = 0;

          // 通过最终结果的MarshalList字段加到全局也是反序列化出来的MarshalList

          CMarshalList::AddMarshal(CBasedMarshalListObjNext, &CExposedDocFile_MarshalList_Final->CMarshalListObj);

          // 最终结果是ExposedDoc

        }

        if ( SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr )

          // PubDocFile也反序列化出来因为2个doc有关联所以需要addref下PubDocFile

          CPubDocFileObjNext = (CPubDocFile *)(SDfMarshalPacketCurrent.CBasedPubDocFileObj._SelftobjectPtr

                                             + *(_DWORD *)NtCurrentTeb()->ReservedForOle);

        else

          CPubDocFileObjNext = 0;

        InterlockedIncrement(&CPubDocFileObjNext->_count_References);

      }

      // 把CExposedDocFile_MarshalList_Final赋值给最终需要反序列化的远程对象指针ppv

      *ppv = CExposedDocFile_MarshalList_Final;

      CDfMutex::Release(&mtx);

      if ( pvBaseOld != ReservedForOleRef )

      {

        CPerContext::SetThreadAllocatorState(&pcSharedMemory, 0);

        CSmAllocator_C = GetTlsSmAllocator();

        CSmAllocator::Uninit(CSmAllocator_C);

      }

      CSmAllocator_D = GetTlsSmAllocator();

      CSmAllocator::SetState(CSmAllocator_D, 0, 0, 0, 0, 0);

      if ( pstorgeStd )

        pstorgeStd->_SelfStorageVtbl->Release(pstorgeStd);

      goto LABEL_80;

    }

LABEL_54:

    hrFinal = -2147287032;

    CPerContext::Release(CPerContextObj);

    goto EH_mtx_0;

  }

EH_std_0:

  if ( !pstorgeStd )

  {

    CPerContext::~CPerContext(&pcSharedMemory);

    CDfMutex::~CDfMutex(&mtx);

    return hrFinal;

  }

  *ppv = pstorgeStd;

LABEL_80:

  CPerContext::~CPerContext(&pcSharedMemory);

  CDfMutex::~CDfMutex(&mtx);

  return 0;

}

DfUnMarshalInterface默认是采用MSHCTX=MSHCTX_INPROC也就是同进程内Unmrahsl方式读取的指针都是的同进程对象,不过它也支持UnmarshalSharedMemory方式,也就是从结构的hMem字段从audiodg.exe的共享内存初始化相应的共享对象

//SDfMarshalPacket结构

typedef  struct _SDfMarshalPacket

{

    unsigned int CBasedPubDocFileObj;

    void *  CBasedPubStreamObj;

    void *  CBasedSeekPointerObj;

    void *  CBasedMarshalListObj;

    void *  CBasedDFBasisObj;

    unsigned int CBasedGlobalContextObj;

    unsigned int CBasedGlobalFileStreamBaseObj;

    void *  CBasedGlobalFileStreamDirty;

    void *  CBasedGlobalFileStreamOriginal;

    unsigned int ulHeapName;

    unsigned int ProcessContextId;

    GUID cntxkey;

    CPerContext * CPerContextObj;

    //共享内存

    void *hMem;

} SDfMarshalPacket;

windows音频服务允许建立一个Initialize在audiodg.exe进程共享内存中AUDCLNT_SHAREMODE=AUDCLNT_SHAREMODE_SHARED的共享内存,并开辟这样一块共享内存给poc进程和bits进程使用.通过ntdll未公开的函数实现找到位于audiodg.exe进程共享内存的句柄,写入交换数据后,将共享内存的句柄值写入SDfMarshalPacket->hMem,关于ntdll未公开的函数具体实现poc中有代码请读者自行研究.

HRESULT STDMETHODCALLTYPE SharedMemoryMarshaller::StartAudioClient(){

        const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);

        const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);

        IMMDeviceEnumeratorPtr pEnumerator = nullptr;

        HRESULT hr = CoCreateInstance(

            CLSID_MMDeviceEnumerator, NULL,

            CLSCTX_ALL, IID_IMMDeviceEnumerator,

            (void**)&pEnumerator);

        IMMDevicePtr pDevice = nullptr;

        hr = pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eConsole, &pDevice);

        WAVEFORMATEX *pwfx = NULL;

        REFERENCE_TIME hnsRequestedDuration = 10000000;

        UINT32 nFrames = 0;

        IAudioClient *pAudioClient = NULL;

        // Get the audio client.

        (hr = pDevice->Activate(

            __uuidof(IAudioClient),

            CLSCTX_INPROC_SERVER,

            NULL,

            (void**)&pAudioClient));

        // Get the device format.

        hr = pAudioClient->GetMixFormat(&pwfx);

        // Open the stream and associate it with an audio session.

        hr = pAudioClient->Initialize(

            AUDCLNT_SHAREMODE_SHARED,

            NULL,

            hnsRequestedDuration,

            0,

            pwfx,

            NULL);

        return hr;

    }

在CSharedMemoryBlock::InitUnMarshal中CSharedMemoryBlockObj->_pbBase被赋值成了SDfMarshalPacket->hMem共享内存映射后的地址,之后在CSmAllocator::Init中 CPerContext->_pbBase = CSharedMemoryBlockObj->_pbBase + 8,之后在UnmarshalSharedMemory接着就调用CPerContext::SetThreadAllocatorState接着调用CSmAllocator::SetState将NtCurrentTeb()->ReservedForOle赋值成CPerContext->_pbBase.最终得出的结论是NtCurrentTeb()->ReservedForOle-8就是共享内存内存映射的相对地址.在poc中使用如下方式操作共享内存
在poc中映射的共享内存基址directoryMappedAddressLocal就是bits进程中NtCurrentTeb()->ReservedForOle-8即bits进程映射的共享内存基址.以下是poc操作共享内存的方法
CBasedMapSectionPtr pdf = (CBasedMapSectionPtr)(pck->CBasedPubDocFileObj + (ULONG)directoryMappedAddressLocal + 8);
pdf->_SelftobjectPtr = 0x46444250;

void __stdcall UnmarshalSharedMemory(SDfMarshalPacket *SDfMarshalPacketCurrent, unsigned int mshlflags, CPerContext *ppcOwner)

{

  SDfMarshalPacket *pck; // esi

  unsigned int HeapName; // ebx

  CPerContext *CPerContextObjTemp; // eax

  HANDLE currentThreadHandle; // eax

  CPerContext *ppcOwnerRef; // ecx

  unsigned int ProcessContextIdRef; // [esp+Ch] [ebp-Ch]

  void *hToken; // [esp+10h] [ebp-8h]

  int sc; // [esp+14h] [ebp-4h]

  CSmAllocator *pMalloc; // [esp+20h] [ebp+8h]

  CSmAllocator *pMalloca; // [esp+20h] [ebp+8h]

  pck = SDfMarshalPacketCurrent;

  HeapName = SDfMarshalPacketCurrent->ulHeapName;

  // 获取当前进程pid

  ProcessContextIdRef = SDfMarshalPacketCurrent->ProcessContextId;

  // PerContextObj反序列出来如果是当前进程就取它的值

  CPerContextObjTemp = SDfMarshalPacketCurrent->CPerContextObj;

  sc = 0;

  //  pMalloc 就是CPerContextObjTemp的引用

  pMalloc = (CSmAllocator *)CPerContextObjTemp;

  if ( GetCurrentProcessId() == ProcessContextIdRef )

  {

    // CSmAllocatorObjRefThis就是去反序列化这和sharedmemeory没关系了,默认认为sharedmemeory已经存在并初始化

    ppcOwnerRef = (CPerContext *)pMalloc;

  }

  else

  {

    pMalloca = GetTlsSmAllocator();

    if ( pMalloca->_ulHeapName != HeapName )

    {

      hToken = 0;

      // 获取当前线程Thread的tid

      currentThreadHandle = GetCurrentThread();

      if ( OpenThreadToken(currentThreadHandle, 8u, 1, &hToken) )

      {

        CloseHandle(hToken);

        sc = 0x80030005;

        JUMPOUT(&requireCreateInstance);

      }

      // 非当前进程认为sharedmemeory不存在并开始初始化

      CSmAllocator::SetState(pMalloca, 0, 0, 0, 0, 0);

      // fUnmarshal=1,开始Unmarshal共享内存

      sc = CSmAllocator::Init(pMalloca, pck->hMem, ProcessContextIdRef, HeapName, 1);     

      sc = CSmAllocator::Sync(pMalloca);     

    }

    // owner就是pcSharedMemory如果非当前进程,CPerContext的pbBase又赋值给CSmAllocator的pbBase

    CPerContext::GetThreadAllocatorState(ppcOwner);

    ppcOwnerRef = ppcOwner;

  }

  // 设置一下状态

  CPerContext::SetThreadAllocatorState(ppcOwnerRef, 0); 

}

void __thiscall CSmAllocator::SetState(CSmAllocator *this, CSharedMemoryBlock *psmb, char *pbBase, unsigned int ulHeapName, CPerContext **ppcPrev, CPerContext *ppcOwner)

{

  unsigned int v6; // eax

  // that->CSharedMemoryBlockObj(+0x4) = shmBlock;

  this->CSharedMemoryBlockObj = psmb;

  //   this->_pbBase(0x8偏移量) = pbBase;

  this->_pbBase = pbBase;

  if ( psmb )

    v6 = psmb->_culCommitSize - 8;

  else

    v6 = 0;

  this->_cbSize = v6;

  this->_ulHeapName = ulHeapName;

  *(_DWORD *)NtCurrentTeb()->ReservedForOle = pbBase;

  if ( ppcPrev )

    *ppcPrev = this->_ppcOwner;

  this->_ppcOwner = ppcOwner;

}

int __thiscall CSmAllocator::Init(CSmAllocator *this, void *hMem, unsigned int dwProcessId, unsigned int ulHeapName, int fUnmarshal)

{

  CSharedMemoryBlock *shmBlock; // eax

  CSharedMemoryBlock *shmBlockRef; // edi

....

  shmBlock = (CSharedMemoryBlock *)operator new(0x18u);

  if ( shmBlock )

  {

    shmBlock->_hMem = 0;

    shmBlock->_pbBase = 0;

    shmBlock->_culCommitSize = 0;

    shmBlock->_culInitCommitSize = 0;

    shmBlock->_fCreated = 1;

    shmBlock->_fReadWrite = 0;

  }

  else

  {

    shmBlock = 0;

  }

  shmBlockRef = shmBlock;

  // that->CSharedMemoryBlockObj(+0x4)偏移量 = shmBlock;

  that->CSharedMemoryBlockObj = shmBlock;

  if ( shmBlock )

  {

    if ( fUnmarshal )

    {

      hrTemp = CSharedMemoryBlock::InitUnMarshal(shmBlock, hMem, dwProcessId, 0x3FF8u);

      goto LABEL_13;

    }

LABEL_20:

    hrTemp = CSharedMemoryBlock::Init(shmBlockRef, 0, 0x3FFFF8u, 0x3FF8u, 0, 0, 1);

LABEL_13:

    sc = hrTemp;

    if ( hrTemp < 0 )

    {

      if ( shmBlockRef != &g_smb && shmBlockRef )

        CSharedMemoryBlock::`scalar deleting destructor'(shmBlockRef, 1u);

      that->CSharedMemoryBlockObj = 0;

      return sc;

    }

    that->_cbSize = shmBlockRef->_culCommitSize - 8;

    // pMalloca的pbBase就是NtCurrentTeb()->ReservedForOle->pvThreadBase(0x0偏移量)值相等,shmBlockRef->_pbBase也就是共享内存分配的基址,其中pMalloca=CPerContext

    that->_pbBase = shmBlockRef->_pbBase + 8;

   .....

}

int __thiscall CSharedMemoryBlock::InitUnMarshal(CSharedMemoryBlock *this, void *hMem, unsigned int dwProcessId, unsigned int culCommitSize)

{

  CSharedMemoryBlock *that; // esi

  int v5; // ebx

  signed int v6; // eax

  HANDLE v8; // eax

  int v9; // eax

  char *shareMemoryOffset; // eax

  int v11; // eax

  HANDLE hProcess; // [esp+18h] [ebp+Ch]

  that = this;

  v5 = 0;

  hProcess = OpenProcess(0x40u, 0, dwProcessId);

  if ( hProcess )

  {

    v8 = GetCurrentProcess();

    // &that->_hMem=0x偏移量

    if ( DuplicateHandle(hProcess, hMem, v8, &that->_hMem, 0, 0, 2u) )

    {

      shareMemoryOffset = (char *)MapViewOfFileEx(that->_hMem, 6u, 0, 0, 0, 0);

      // //pMalloca->_pbBase = shmBlockRef->_pbBase + 8; 其中的pMalloca->pbBase就是NtCurrentTeb()->ReservedForOle->pvThreadBase(0x0偏移量)值相等,shmBlockRef->_pbBase= that->_pbBase也就是共享内存分配的基址

      that->_pbBase = shareMemoryOffset;

.....

}

在windbg验证结论,共享内存CPerContext的地址为0617a350,可见它继承与CSmAllocator,它的CPerContext->CSharedMemoryBlockObj(+0x4)也就是CSharedMemoryBlock地址为060def40,CSharedMemoryBlock->_pbBase地址为03e40000,同时可以看到ReservedForOle指针指向的地址为03e40008,验证了结论.

0:018> dt ntdll!_TEB ReservedForOle @$teb 

   +0xf80 ReservedForOle : 0x031a9e98 Void

0:018> dc 0x031a9e98

031a9e98  03e40008 0617a350 00000000 00001002  ....P...........

031a9ea8  0000000c 02f86f80 00000000 00000000  .....o..........

031a9eb8  00000001 03112648 0506e850 00000000  ....H&..P.......

031a9ec8  00000000 00002f4c 03109f80 03109f80  ....L/..........

031a9ed8  00000000 00000000 0000013c 00000000  ........<.......

031a9ee8  00000000 0313c658 00000000 00000000  ....X...........

031a9ef8  00000000 00000000 00000000 00000000  ................

031a9f08  00000000 00000000 00000000 00000000  ................

0:018> dps 0x031a9e98

//ReservedForOle指针指向的地址=CSharedMemoryBlock->_pbBase+8

031a9e98  03e40008

//CPerContext的地址

031a9e9c  0617a350

031a9ea0  00000000

031a9ea4  00001002

031a9ea8  0000000c

031a9eac  02f86f80

...

//CPerContext的地址为0617a350

0:018> dps  0617a350

0617a350  750c11a8 coml2!CSmAllocator::`vftable'

//CPerContext->_psmb也就是CSharedMemoryBlock地址

0617a354  060def40

//NtCurrentTeb()->ReservedForOle赋值成CPerContext->_pbBase

0617a358  03e40008

....

//CSharedMemoryBlock地址

0:018> dps 060def40

060def40  000010b8

//CSharedMemoryBlock->_pbBase地址

060def44  03e40000

...

设置好共享内存后会进入UnmarshalContext,其中先从SDfMarshalPacket->CBasedGlobalContextObj中通过CContextList::_Find找第一个CGlobalContext,其实CGlobalContext继承于CContextList结构,会根据CContextList->pctxNext找下一个CContextList直到CContextList->ctxid为要找的进程id为止.之后会验证下CPerContext句柄是否有效,其实只要构造一个名为"\Sessions\0\BaseNamedObjects\OleDfRoot%X%08lX"的内核Event通过NtCreateEvent,Bits服务的Sessions为0,详见poc代码.

SDfMarshalPacket *__userpurge UnmarshalContext@<eax>(CPerContext **a1@<edx>, SDfMarshalPacket *pckfrom@<ecx>, struct SDfMarshalPacket *pvkOrg, struct CPerContext **a4, unsigned int a5, int a6, int a7)

{

  SDfMarshalPacket *pck; // edi

  CGlobalContext *pCGlobalContextOffset; // ecx

  CGlobalContext *pCGlobalContext; // esi

  unsigned int NowPid; // eax

  CPerContext *pCPerContextFound; // eax

  CContext *pContext; // ebx

  struct _GUID v13; // ST04_16

  struct CPerContext **v14; // ecx

  CPerContext *v15; // eax

  CSmAllocator *v17; // eax

  unsigned int NowPidNext; // eax

  int CBasedGlobalFileStreamBaseObjOffset; // edx

  CGlobalFileStream *pCBasedGlobalFileStreamBase; // ecx

  int v21; // eax

  char *v22; // edx

  int v23; // esi

  CGlobalFileStream *fstmFromGlobal; // ecx

  SDfMarshalPacket *v25; // eax

  char *v26; // edx

  CGlobalFileStream *v27; // ecx

  SDfMarshalPacket *v28; // eax

  struct ILockBytes *v29; // edi

  struct CFileStream *v30; // ebx

  struct ILockBytes *v31; // esi

  unsigned int v32; // edx

  void **v33; // [esp+0h] [ebp-44h]

  void **v34; // [esp+0h] [ebp-44h]

  void **v35; // [esp+0h] [ebp-44h]

  struct IMalloc *v36; // [esp+4h] [ebp-40h]

  unsigned int v37; // [esp+4h] [ebp-40h]

  unsigned int *v38; // [esp+4h] [ebp-40h]

  CPerContext **v39; // [esp+10h] [ebp-34h]

  CPerContext *v40; // [esp+14h] [ebp-30h]

  int v41; // [esp+18h] [ebp-2Ch]

  unsigned int v42; // [esp+1Ch] [ebp-28h]

  BOOL pCPerContextFoundRef; // [esp+20h] [ebp-24h]

  CGlobalContext *pCGlobalContextRef; // [esp+24h] [ebp-20h]

  struct CFileStream *v45; // [esp+28h] [ebp-1Ch]

  struct ILockBytes *ppvRet; // [esp+2Ch] [ebp-18h]

  struct ILockBytes *v47; // [esp+30h] [ebp-14h]

  struct CFileStream *v48; // [esp+34h] [ebp-10h]

  struct ILockBytes *v49; // [esp+38h] [ebp-Ch]

  SDfMarshalPacket *pckRef; // [esp+3Ch] [ebp-8h]

  v39 = a1;

  v42 = 0;

  pck = pckfrom;

  v41 = 0;

  pckRef = pckfrom;

  ppvRet = 0;

  v48 = 0;

  pCGlobalContextOffset = pckfrom->CBasedGlobalContextObj;

  v45 = 0;

  v49 = 0;

  v47 = 0;

  v40 = 0;

  if ( pCGlobalContextOffset )

  {

    pCGlobalContext = (pCGlobalContextOffset + *NtCurrentTeb()->ReservedForOle);

    v41 = ppvRet;

    v48 = v45;

    v49 = v47;

  }

  else

  {

    pCGlobalContext = 0;

  }

  pCGlobalContextRef = pCGlobalContext;

  NowPid = GetCurrentProcessId();

  // 找第一个PerContext

  pCPerContextFound = CContextList::_Find(&pCGlobalContext->CContextListbase, NowPid);

  pContext = &pCPerContextFound->baseclass_CContext;

  pCPerContextFoundRef = !pCPerContextFound

                      // 先验证下找到的PerContext必须成功

                      || (pContext = (CPerContext::IsHandleValid(pCPerContextFound) != 0 ? pCPerContextFound : 0)) == 0;

  // 如果不是当前进程检查CProcessSecret,不相符也没关系

  if ( GetCurrentProcessId() != pck->ProcessContextId )

  {

    *&v13.Data1 = *&pck->cntxkey.Data1;

    *v13.Data4 = *pck->cntxkey.Data4;

    if ( CProcessSecret::VerifyMatchingSecret(v13) < 0 && pContext )

      // 不相符也没关系只是不设置状态

      CPerContext::SetThreadAllocatorState(pContext, v14);

    pCGlobalContext = pCGlobalContextRef;

    pck = pckRef;

  }

  if ( !pCPerContextFoundRef )

  {

LABEL_21:

    v17 = GetTlsSmAllocator();

    CSmAllocator::SetState(v17, pContext[5].pctxNext, pContext[6].ctxid, pContext[6].pctxNext, 0, pContext);

    NowPidNext = GetCurrentProcessId();

    // 继续找下一个PerContext

    pContext = CContextList::_Find(&pCGlobalContext->CContextListbase, NowPidNext);

    if ( !pContext )

    {

      pckRef = 0x800300FD;

      goto LABEL_23;

    }

    CBasedGlobalFileStreamBaseObjOffset = pck->CBasedGlobalFileStreamBaseObj;

    if ( CBasedGlobalFileStreamBaseObjOffset )

    {

      pCBasedGlobalFileStreamBase = (CBasedGlobalFileStreamBaseObjOffset + *NtCurrentTeb()->ReservedForOle);

      v49 = v47;

    }

    else

    {

      pCBasedGlobalFileStreamBase = 0;

    }

    // 进入关键步骤

    hr = CFileStream::Unmarshal(&ppvRet, pCBasedGlobalFileStreamBase, pCBasedGlobalFileStreamBase, v33, v36);

   ....

}

struct CContext *__thiscall CContextList::_Find(CContextList *this, unsigned int pid)

{

  CContext *HeadCtx; // eax

  char *HeadCtxFoundRet; // edx

  CContext *v4; // ecx

  CContext *HeadCtxlookup; // ecx

  CContext *v6; // ecx

  CContext *HeadCtxFound; // ecx

  HeadCtx = this->_pctxHead;

  HeadCtxFoundRet = 0;

  while ( 1 )

  {

    v4 = HeadCtx ? (HeadCtx + *NtCurrentTeb()->ReservedForOle) : 0;

    if ( !v4 )

      break;

    HeadCtxlookup = HeadCtx ? (HeadCtx + *NtCurrentTeb()->ReservedForOle) : 0;

    if ( HeadCtxlookup->ctxid )

    {

      v6 = HeadCtx ? (HeadCtx + *NtCurrentTeb()->ReservedForOle) : 0;

      if ( v6->ctxid == pid )

        break;

    }

    if ( HeadCtx )

      HeadCtxFound = (HeadCtx + *NtCurrentTeb()->ReservedForOle);

    else

      HeadCtxFound = 0;

    // 继续找下一个

    HeadCtx = HeadCtxFound->pctxNext;

  }

  if ( HeadCtx )

    HeadCtxFoundRet = HeadCtx + *NtCurrentTeb()->ReservedForOle;

  return HeadCtxFoundRet;

}

int __thiscall CPerContext::IsHandleValid(CPerContext *this)

{

  CPerContext *that; // esi

  int result; // eax

  wchar_t Dest; // [esp+8h] [ebp-48h]

  that = this;

  StringCchPrintfW(

    &Dest,

    0x20u,

    L"OleDfRoot%X%08lX",

    this->CGlobalContextPtr->_luidMutexName.HighPart,

    this->CGlobalContextPtr->_luidMutexName.LowPart);

  // 在poc中使用NtCreateEvent使它验证成功

  result = CDfMutex::IsHandleValid(&that->_dmtx, &Dest);

  if ( !result )

    // 失败就返回0

    that->baseclass_CContext.ctxid = 0;

  return result;

}

之后进入 CFileStream::Unmarshal,同样通过CContextList::_Find和方式通过设置和bits服务相同的pid从SDfMarshalPacket->CBasedGlobalFileStreamBaseObj找第一个CFileStream也就是poc中往共享内存0x7279 - 0x10写入的CFileStream,它的CFileStream->_hFile被赋值为0导致GetFileType返回-1导致验证失败(不影响程序运行),这个时候它的ctxid已经可以检测到为0,这时会进入poc中新建线程的第一个if语句断点.由于验证失败进入else,其中fstmOut->_CGlobalFileStreamPtr被赋值成SDfMarshalPacket->CBasedGlobalFileStreamBaseObj,fstmGlobal赋值成SDfMarshalPacket->CGlobalFileStreamPtr,之后它的fstmGlobal->_CGlobalFileStreamPtr->_pctxHead=0x7279被 CContextList::Add设置成fstmGlobal的相对地址,可以去判断读取SDfMarshalPacket->CBasedGlobalFileStreamBaseObj->_pctxHead已经不是0x7279,其实是fstmGlobal的相对地址.笔者发现一个小tips,fstmGlobal->_CGlobalFileStreamPtr也是可以在SDfMarshalPacket中读取到的,这样实际上求fstmGlobal->_CGlobalFileStreamPtr-SDfMarshalPacket->CBasedGlobalFileStreamBaseObj值就可以计算出bits进程的映射共享内存的映射基址也就是(NtCurrentTeb()->ReservedForOle->pvThreadBase-8)的值,从而实现读写bits进程的任意指定真实地址(非通过偏移内存计算)内存数据在共享内存区域,有兴趣的读者可以自行尝试后续研究.

int __userpurge CFileStream::Unmarshal@<eax>(unsigned int *ppvRet@<edx>, CGlobalFileStream *fstmFromGlobal@<ecx>, struct CGlobalFileStream *a3, void **a4, unsigned int a5)

{

  CGlobalFileStream *fstmGlobal; // ebx

  unsigned int nowPid; // eax

  struct CContext *pCPerContextFound; // eax

  CFileStream *fstm; // esi

  CFileStream *fstmOut; // edi

  int hr; // eax

  int v11; // ebx

  CFileStream *CFileStreamAlloced; // eax

  unsigned int v14; // [esp+0h] [ebp-14h]

  struct IMalloc *v15; // [esp+4h] [ebp-10h]

  unsigned int *v16; // [esp+Ch] [ebp-8h]

  v16 = ppvRet;

  fstmGlobal = fstmFromGlobal;

  nowPid = GetCurrentProcessId();

  pCPerContextFound = CContextList::_Find(&fstmGlobal->_pctxHead, nowPid);

  if ( pCPerContextFound )

    // 就是poc的 CFileStream fstm=(CFileStream*)(0x7279 - 0x10 + (ULONG)directoryMappedAddressLocal + 8);

    fstm = &pCPerContextFound[-2];

  else

    fstm = 0;

  // 验证句柄,这里在poc中新建线程中检测到that->baseclass_CContext.ctxid=0

  if ( fstm && (fstmOut = (CFileStream::IsHandleValid(fstm) != 0 ? fstm : 0)) != 0 )

  {

    // 这部在原作者的hajctvtable中实现

    (fstmOut->_ILockBytesField.lpVtbl->AddRef)(fstmOut->_ILockBytesField.lpVtbl->AddRef, fstmOut);

    if ( !fstmGlobal->_awcPath[0] )

    {

LABEL_9:

      *v16 = fstmOut;

      return 0;

    }

    hr = CFileStream::InitWorker(fstmOut, 0, 1u, 0);

  }

  else

  {

    // 创建新的CFileStream

    CFileStreamAlloced = CMallocBased::operator new(v14, v15);

    if ( CFileStreamAlloced )

      fstmOut = CFileStream::CFileStream(CFileStreamAlloced, fstmGlobal->_pMalloc);

    else

      fstmOut = 0;

    if ( !fstmOut )

      return -2147287032;

    //其中fstmGlobal->_CGlobalFileStreamPt->_pctxHead=0x7279被poc设置,fstmGlobal=CGlobalFileStreamPtr,实际上就可以度bits进程的任意内存数据,因为可以根据CGlobalFileStreamPtr计算出映射共享内存的基址CGlobalFileStreamPtr-olebae-8

    fstmOut->_CGlobalFileStreamPtr = fstmGlobal;

    ++fstmGlobal->_cReferences;

    // (CContextList)(fstmGlobal->_pctxHead(0x0))->_pctxHead(0x0)= &fstmOut->baseContext(0x10)-OleBase;

    // fstmOut=new_fs_offset = fsBase->baseclass_CContextList._pctxHead - 0x10;

    // 创建新的CFileStream链接到(CContextList)(fstmGlobal->_pctxHead(0x0))->_pctxHead(0x0)也就是原来的0x7279

    CContextList::Add(&fstmOut->_CGlobalFileStreamPtr->_pctxHead, &fstmOut->baseContext);

    // 这里之前就要设置_awcPath[0]不为NULL

    if ( !fstmGlobal->_awcPath[0] )

      goto LABEL_9;

    // 进入关键步骤

    hr = CFileStream::InitWorker(fstmOut, 0, 1u, 0);

  }

  v11 = hr;

  if ( hr >= 0 )

    goto LABEL_9;

  (fstmOut->_ILockBytesField.lpVtbl->Release)(fstmOut->_ILockBytesField.lpVtbl->Release, fstmOut);

  return v11;

}

int __thiscall CFileStream::IsHandleValid(CFileStream *this)

{

  CFileStream *that; // edi

  signed int hr; // esi

  that = this;

  hr = 1;

  // 这里是hFile要预先设为0让他返回-1

  if ( this->_hFile != -1 && GetFileType(this->_hFile) != 1 )

  {

    hr = 0;

    // 这里在poc中新建线程中检测到that->baseclass_CContext.ctxid=0

    that->baseContext.ctxid = 0;

  }

  return hr;

}

之后进入CFileStream::InitWorker,that->_CGlobalFileStreamPtr->_pctxHead=0x7279,(CFileStream*)(0x7279 - 0x10)是第一个找到的fstmFoundNew,由于的我在poc中预先设置了
fstmFoundNew->baseContext.pctxNext = 0x7279这样pctxNext又链接到了它自己,这样可以构建 while ( fstmFoundNew )无限循环,产生一个时间差,让poc中的新建线程有时间读取最终复制的句柄.

int __thiscall CFileStream::InitWorker(CFileStream *this, const unsigned __int16 *a2, unsigned int a3, void *a4)

{

  CFileStream *that; // esi

  CGlobalFileStream *fstmGlobal; // eax

  unsigned int v6; // ebx

  int hrTemp; // eax

  signed int v8; // edi

  unsigned int v9; // ecx

  CGlobalFileStream *v10; // eax

  const unsigned __int16 *v12; // ecx

  CGlobalFileStream *v13; // eax

  unsigned int v14; // [esp+0h] [ebp-43Ch]

  const unsigned __int16 *v15; // [esp+4h] [ebp-438h]

  LPWSTR FilePart; // [esp+10h] [ebp-42Ch]

  WCHAR Buffer; // [esp+14h] [ebp-428h]

  WCHAR FileName; // [esp+224h] [ebp-218h]

  // this=new 0x7279

  that = this;

  FilePart = a4;

  fstmGlobal = this->_CGlobalFileStreamPtr;

  v6 = fstmGlobal->_df;

  // 这里之前就要设置hfile=-1,之后会被赋值

  if ( this->_hFile != -1 )

    return 0;

  // 这里之前就要设置_awcPath[0]不为NULL

  if ( fstmGlobal->_awcPath[0] )

  {

    // 进入复制句柄方法

    hrTemp = CFileStream::Init_DupFileHandle(this, 0);

  }

 ....

}

int __thiscall CFileStream::Init_DupFileHandle(CFileStream *this, unsigned int a2)

{

  CFileStream *that; // eax

  HANDLE fakePid; // edi

  void *hPreDupedRef; // ecx

  int fstmFoundNewOffset; // ecx

  CFileStream *fstm; // esi

  CFileStream *fstmFoundNew; // esi

  void *hfileRef; // ebx

  HANDLE bitsPid; // eax

  CFileStream *_this; // [esp+8h] [ebp-4h]

  that = this;

  fakePid = 0;

  _this = this;

  hPreDupedRef = this->_hPreDuped;

  // hPreDuped之前也必需要是-1

  if ( hPreDupedRef != -1 )

  {

    that->_hPreDuped = -1;

    that->_hFile = hPreDupedRef;

    return 0;

  }

  // fstmFoundNewOffset是原CGlobalFileStreamPtr->_pctxHead=0x7279的CFileStream,不是是被替换后新创建的CFileStream,便于被查找next进入循环

  fstmFoundNewOffset = that->_CGlobalFileStreamPtr->_pctxHead;

  if ( fstmFoundNewOffset )

    fstm = (fstmFoundNewOffset + *NtCurrentTeb()->ReservedForOle);

  else

    fstm = 0;

  if ( fstm )

    // fstmOut=new_fs_offset  that->_CGlobalFileStreamPtr._pctxHead - 0x10;

    // 找到后再- 0x10

    fstmFoundNew = (fstm - 0x10);

  else

    fstmFoundNew = 0;

  if ( !fstmFoundNew )

    return -2147287034;

  do

  {

    hfileRef = fstmFoundNew->_hFile;

    if ( hfileRef == -1 )

    {

      // 如果hFile还是-1就继续找下一个

      if ( fstmFoundNew->_hPreDuped == -1 )

        goto LABEL_17;

      hfileRef = fstmFoundNew->_hPreDuped;

    }

    // 根据ctxid打开poc进程

    fakePid = OpenProcess(0x40u, 0, fstmFoundNew->baseContext.ctxid);

    if ( fakePid )

    {

      bitsPid = GetCurrentProcess();

      // 赋值poc进程的句柄至bits进程句柄写入CFileStream->_hFile

      if ( DuplicateHandle(fakePid, hfileRef, bitsPid, &_this->_hFile, 0, 0, 2u) )

        break;

      GetLastError();

      _this->_hFile = -1;

      CloseHandle(fakePid);

      fakePid = 0;

    }

    else

    {

      GetLastError();

    }

LABEL_17:

    // 找Next的方式就是new_fs_offset = CFileStream->baseContext.pctxNext - 0x10,这里Next预先链接为自己,这样可以构建时间差,但poc现成有时间读取最终复制的句柄

    fstmFoundNew = CFileStream::GetNext(fstmFoundNew);

  }

  while ( fstmFoundNew );

  if ( !fstmFoundNew )

    return -2147287034;

  if ( fakePid )

    CloseHandle(fakePid);

  return 0;

}

最终bits服务调用DuplicateHandle函数复制了poc中pWorkFileStream->_hFile设置的句柄,使用这个句柄后作为创建新进程的父句柄,最后成功弹出了一个System权限NotePad,如图:

引用

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功