Windows error resporting service中存在权限提升漏洞,当攻击者可以创建符号链接及目录时,可以利用这个漏洞提升至SYSTEM权限。
略
补丁对比 diff wercplsupport.dll,主要改了CWerComReport::SubmitReport,wercplsupport.dll是Windows error reporting 服务的主dll文件。

对比发现补丁直接阻断了后续CAutoImpersonate::ImpersonateUserHighestPrivs和CWerComReport::_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为
查看这个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::ReportProblem,CReportManager::ReportProblem由ReportHandleInstance::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);
.....
}
_SubmitReport由 CWerComReport::SubmitReport调用,而 CWerComReport::SubmitReport为IWerReport接口公开的函数。
__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