CVE-2023-36874 Windows Error Reporting Service 权限提升漏洞分析

基本信息

Windows error resporting service中存在权限提升漏洞,当攻击者可以创建符号链接及目录时,可以利用这个漏洞提升至SYSTEM权限。

影响版本

环境搭建

  • Windows 10 21H2 6月补丁

技术分析&调试

补丁对比 diff wercplsupport.dll,主要改了CWerComReport::SubmitReport,wercplsupport.dll是Windows error reporting 服务的主dll文件。

对比发现补丁直接阻断了后续CAutoImpersonate::ImpersonateUserHighestPrivsCWerComReport::_SubmitReport的调用

//未修复
__int64 __fastcall CWerComReport::SubmitReport(
        CWerComReport *this,
        unsigned __int16 *a2,
        unsigned int a3,
        struct IWerReportSubmitCallback *a4,
        unsigned __int16 **a5,
        unsigned int *a6)
{
  int v10; // ebx
  int v12; // [rsp+30h] [rbp-18h] BYREF
  __int64 v13; // [rsp+38h] [rbp-10h]

  v13 = -2i64;
  v12 = 2;
  if ( !CAutoImpersonate::g_bEnableImpersonate
    || (v10 = CAutoImpersonate::ImpersonateUserHighestPrivs((CAutoImpersonate *)&v12), v10 >= 0) )
  {
    v10 = CWerComReport::_SubmitReport((CWerComReport *)((char *)this - 24), a2, a3, a4, a5, a6);
  }
  CAutoImpersonate::~CAutoImpersonate((CAutoImpersonate *)&v12);
  return (unsigned int)v10;
}

// 修复代码
__int64 __fastcall CWerComReport::SubmitReport(
        CWerComReport *this,
        unsigned __int16 *a2,
        unsigned int a3,
        struct IWerReportSubmitCallback *a4,
        unsigned __int16 **a5,
        unsigned int *a6)
{
  int v11; // ebx
  int v12; // [rsp+30h] [rbp-18h] BYREF
  __int64 v13; // [rsp+38h] [rbp-10h]

  v13 = -2i64;
  if ( (unsigned __int8)wil::details::FeatureImpl<__WilFeatureTraits_Feature_MSRC80633_DisableWerCplSupport>::__private_IsEnabled(&`wil::Feature<__WilFeatureTraits_Feature_MSRC80633_DisableWerCplSupport>::GetImpl'::`2'::impl) )
    return 0x80004001i64;
  v12 = 2;
  if ( !CAutoImpersonate::g_bEnableImpersonate
    || (v11 = CAutoImpersonate::ImpersonateUserHighestPrivs((CAutoImpersonate *)&v12), v11 >= 0) )
  {
    v11 = CWerComReport::_SubmitReport((CWerComReport *)((char *)this - 24), a2, a3, a4, a5, a6);
  }
  CAutoImpersonate::~CAutoImpersonate((CAutoImpersonate *)&v12);
  return (unsigned int)v11;
}

根据函数名CAutoImpersonate::ImpersonateUserHighestPrivs可知,该函数为模拟用户最高的权限并提交report

动态调试

开启Problem Reports Control Panel Support服务,对应路径为C:\Windows\System32\svchost.exe -k netsvcs -p。 使用oleviewdotnet查询Problem Reports Control Panel Support服务对应的ole信息

对应的COM接口的CLSID为

  • CLSID: 0E9A7BB5-F699-4D66-8A47-B919F5B6A1DB
  • AppID: 136A0DC7-DF5C-4271-A2AC-15DF1A1323F2 查看这个COM的接口信息
class __declspec(uuid("6620c14b-70ae-4d4e-a4f6-91a7dcc582c2")) IErcLuaSupport : public IUnknown {
public:
    virtual HRESULT __stdcall Proc3(/* Stack Offset: 8 */ IWerStoreFactory** p0);
};

class __declspec(uuid("4904c154-426f-4c88-8ec2-4543d18670f7")) IWerStoreFactory : public IUnknown {
public:
    virtual HRESULT __stdcall Proc3(/* Stack Offset: 8 */ IWerStore** p0);
    virtual HRESULT __stdcall Proc4(/* Stack Offset: 8 */ IWerStore** p0);
};

class __declspec(uuid("1e3a0e4f-1412-444f-8a94-fc6a09cd4195")) IWerStore : public IUnknown {
public:
    virtual HRESULT __stdcall Proc3();
    virtual HRESULT __stdcall Proc4(/* Stack Offset: 8 */ BSTR* p0);
    virtual HRESULT __stdcall Proc5(/* Stack Offset: 8 */ BSTR p0);
    virtual HRESULT __stdcall Proc6(/* Stack Offset: 8 */ BSTR p0, /* Stack Offset: 16 */ IWerReport** p1);
    virtual HRESULT __stdcall Proc7(/* Stack Offset: 8 */ BSTR p0, /* Stack Offset: 16 */ BSTR* p1);
};

class __declspec(uuid("d01b8f28-0bd1-4652-a415-8229f5ee506c")) IWerReport : public IUnknown {
public:
    virtual HRESULT __stdcall Proc3(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc4(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc5(/* Stack Offset: 8 */ BSTR* p0);
    virtual HRESULT __stdcall Proc6(/* Stack Offset: 8 */ IWerKeyValueList** p0);
    virtual HRESULT __stdcall Proc7(/* Stack Offset: 8 */ IWerKeyValueList** p0);
    virtual HRESULT __stdcall Proc8(/* Stack Offset: 8 */ IWerStringList** p0);
    virtual HRESULT __stdcall Proc9(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc10(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc11(/* Stack Offset: 8 */ BSTR* p0);
    virtual HRESULT __stdcall Proc12(/* Stack Offset: 8 */ BSTR* p0);
    virtual HRESULT __stdcall Proc13(/* Stack Offset: 8 */ IWerStringList** p0);
    virtual HRESULT __stdcall Proc14(/* Stack Offset: 8 */ IWerStringList** p0);
    virtual HRESULT __stdcall Proc15(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc16(/* Stack Offset: 8 */ struct Struct_1* p0);
    virtual HRESULT __stdcall Proc17(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc18(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc19(/* Stack Offset: 8 */ int64_t* p0);
    virtual HRESULT __stdcall Proc20(/* Stack Offset: 8 */ BSTR p0, /* Stack Offset: 16 */ BSTR* p1);
    virtual HRESULT __stdcall Proc21(/* Stack Offset: 8 */ BSTR* p0);
    virtual HRESULT __stdcall Proc22(/* Stack Offset: 8 */ int64_t p0, /* Stack Offset: 16 */ int64_t* p1, /* Stack Offset: 24 */ int64_t* p2, /* Stack Offset: 32 */ BSTR* p3, /* Stack Offset: 40 */ BSTR* p4);
    virtual HRESULT __stdcall Proc23(/* Stack Offset: 8 */ int64_t p0, /* Stack Offset: 16 */ BSTR* p1);
    virtual HRESULT __stdcall Proc24(/* Stack Offset: 8 */ BSTR p0, /* Stack Offset: 16 */ int64_t p1, /* Stack Offset: 24 */ IWerReportSubmitCallback* p2, /* Stack Offset: 32 */ /* unique */BSTR* p3, /* Stack Offset: 40 */ /* unique */int64_t* p4);
    virtual HRESULT __stdcall Proc25();
};

这里需要知道Windows的[[../../../05 Windows/COM模型/COM模型 OVERVIEW|COM模型),COM模型定义了二进制标准,以支持组件复用。将操作系统API抽象成了接口,可以通过接口的标识符实例化COM对象并通过COM对象调用服务接口。即

当使用COM接口调用error reporting 服务并提交错误报告时,error reporting会启动 C:\Windows\System32\wermgr.exe,并且启动时权限为 NT AUTHORITY\SYSTEM

追溯调用栈 查看此事件的调用栈,wer!WerpAuxmdMapFile+0x3887d 处调用了CreateProcessW

wer!WerpAuxmdMapFile+0x3887d位于 UtilLaunchWerManager函数内,代码如下

__int64 __fastcall UtilLaunchWerManager(
        const unsigned __int16 **a1,
        __int64 a2,
        __int64 a3,
        void *a4,
        void **a5,
        void **a6,
        unsigned int a7,
        void **a8)
{
  ....
  WCHAR Buffer[264]; // [rsp+148h] [rbp+40h] BYREF

  v43 = -2i64;
  v39 = a1;
  v8 = a5;
  lpValue = a8;
  memset_0(Buffer, 0, 0x208ui64);
  lpCommandLine[0] = 0i64;
  lpCommandLine[1] = 0i64;
  .....
    goto LABEL_67;
  }
  v11 = StringCchCatW(Buffer, 0x104ui64, L"\\wermgr.exe");
  v12 = v11;
  if ( v11 >= 0 )
  {
    v12 = CString::Sprintf((CString *)lpCommandLine, L"\"%s\" ", Buffer);
    if ( (v12 & 0x80000000) != 0 )
    {
      if ( WPP_GLOBAL_Control != (HKEY)&WPP_GLOBAL_Control && ((_BYTE)WPP_GLOBAL_Control[7] & 1) != 0 )
      {
        WPP_SF_S(*((_QWORD *)WPP_GLOBAL_Control + 2), 20i64, &WPP_80b9a2815f1633611b5141c011dbf465_Traceguids, Buffer);
        goto LABEL_37;
      }
      goto LABEL_38;
    }
    v13 = 0;
    v14 = lpCommandLine[0];
    while ( v13 < 0xE )
    {
      .....
        v19 = CString::Append((CString *)lpCommandLine, v40[0]);
        if ( v19 >= 0 || WPP_GLOBAL_Control == (HKEY)&WPP_GLOBAL_Control || ((_BYTE)WPP_GLOBAL_Control[7] & 1) == 0 )
        {
          v14 = lpCommandLine[0];
        }
        ....
    if ( UpdateProcThreadAttribute(v10, 0, 0x20002ui64, lpValue, 8i64 * a7, 0i64, 0i64) )
    {
      StartupInfo.cb = 112;
      v45 = v10;
      if ( CreateProcessW(Buffer, v14, 0i64, 0i64, 2, 0x80000u, 0i64, 0i64, &StartupInfo, &hObject) )
      {
        v12 = 0;
      }
      ......
}

向上追溯调用栈,UtilLaunchWerManager函数由 CReportManager::ReportProblemOutOfProcess调用, 再上层函数为CReportManager::ReportProblemCReportManager::ReportProblemReportHandleInstance::SubmitReport调用,在上层函数为WerpSubmitReportFromStore。在wecplsupport!DllCanUnloadNew+0x2bf2处调用了wer.dll!WerpSubmitReportFromStore函数。

wercplsupport!DllCanUnloadNew+0x2bf2实际位于 wercplsupport!CWerComReport::_SubmitReport函数内,代码如下。

__int64 __fastcall CWerComReport::_SubmitReport(
        void **this,
        unsigned __int16 *a2,
        unsigned int a3,
        struct IUnknown *a4,
        unsigned __int16 **a5,
        unsigned int *a6)
{
  ......
  v24 = &CStubUI::`vftable';
  if ( a4 )
    ((void (__fastcall *)(struct IUnknown *))a4->lpVtbl->AddRef)(a4);
  v25 = a4;
  v23[0] = 0i64;
  TokenHandle = 0i64;
  v26 = 0;
  v10 = a5;
  if ( a5 )
  {
    SysFreeString(*a5);
    *v10 = 0i64;
  }
  WerApiLock = CWerApiAutoLock::TryGetWerApiLock((CWerApiAutoLock *)v23, (struct CWerComReport *)this);
  if ( WerApiLock >= 0 )
  {
.....
        }
        else
        {
          CurrentThread = GetCurrentThread();
          if ( OpenThreadToken(CurrentThread, 0xF01FFu, 1, &TokenHandle) || GetLastError() == 1008 )
          {
            .....
            }
            else
            {
              WerApiLock = WerpSubmitReportFromStore(
                             *((void **)this[5] + 4),
                             a2,
                             this[4],
                             (struct IReportUI *)((unsigned __int64)&v24 & -(__int64)(a4 != 0i64)),
                             &v21,
                             a3,
                             (enum _WER_SUBMIT_RESULT *)&v20);
.....
}

_SubmitReportCWerComReport::SubmitReport调用,而 CWerComReport::SubmitReportIWerReport接口公开的函数。

__int64 __fastcall CWerComReport::SubmitReport(
        CWerComReport *this,
        unsigned __int16 *a2,
        unsigned int a3,
        struct IWerReportSubmitCallback *a4,
        unsigned __int16 **a5,
        unsigned int *a6)
{
  int v10; // ebx
  int v12; // [rsp+30h] [rbp-18h] BYREF
  __int64 v13; // [rsp+38h] [rbp-10h]

  v13 = -2i64;
  v12 = 2;
  if ( !CAutoImpersonate::g_bEnableImpersonate
    || (v10 = CAutoImpersonate::ImpersonateUserHighestPrivs((CAutoImpersonate *)&v12), v10 >= 0) )
  {
    v10 = CWerComReport::_SubmitReport((CWerComReport *)((char *)this - 24), a2, a3, a4, a5, a6);
  }
  CAutoImpersonate::~CAutoImpersonate((CAutoImpersonate *)&v12);
  return (unsigned int)v10;
}

所以可以总结出调用链:wecplsupport!CWerComReport::SubmitReport->wecplsupport!CWerComReport::_SubmitReport->wer.dll!WerpSubmitReportFromStore...->CreateProcessW

问题在于在调用CreateProcessW时,CreateProcessW会使用攻击者设置的文件重定向,但将使用调用CreateProcessW的进程的security token设置进程的context,而不是模拟token来设置进程的security context。

也就是攻击者可以通过文件重定向将 C:\Windows\System32重定向到攻击者可控目录,并且在可控目录写入恶意 wermgr.exe,当触发CreateProcessW时,CreateProcessW将使用攻击者控制的目录的wermgr.exe文件而不是系统在C:\Windows\System32目录下的wermgr.exe文件。并且该进程上下文继承了调用进程的上下文,即继承了wer服务的权限。

动态调试UtilLaunchWerManager断点

bp wer!UtilLaunchWerManager

调试器断下

0:006> g
Breakpoint 2 hit
wer!UtilLaunchWerManager+0xf3:
00007ffb`7b11a23f e87cb3f7ff      call    wer!StringCchCatW (00007ffb`7b0955c0)
0:006> rrcx
rcx=00000041e2efbce0

补丁分析 前面知道补丁直接阻断了后续调用_submit,也就没办法再调用CreateProcess,从而阻断了调用链。

PoC

https://github.com/Wh04m1001/CVE-2023-36874

需要注意的是运行poc需要使用不在admin组的用户,新增用户运行

net user test 123456 /add

参考链接

https://www.crowdstrike.com/blog/falcon-complete-zero-day-exploit-cve-2023-36874/

Created at 2023-09-19T10:26:14+08:00

创建于:Tuesday, September 19,2023
最后修改于: Sunday, October 8,2023