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句柄是否有效,其实只要构造一个名为”SessionsBaseNamedObjectsOleDfRoot%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,如图:

查看大图

引用

我的poc地址

原poc

P0地址

(完)