https://offensivedefence.co.uk/posts/assembly-resolve/
文档:
https://docs.microsoft.com/zh-cn/dotnet/framework/unmanaged-api/
poc:
https://gist.github.com/Arno0x/386ebfebd78ee4f0cbbbb2a7c4405f74
blog:
https://thewover.github.io/Introducing-Donut/
https://thewover.github.io/Apple-Fritter/
https://thewover.github.io/Bear-Claw/
其他:
https://blog.ropnop.com/hosting-clr-in-golang/
https://www.mode19.net/posts/clrhostingright/
https://github.com/Accenture/CLRvoyance
https://github.com/N4kedTurtle/ExecuteAssembly_Mailslot
https://teamhydra.blog/2020/10/12/in-process-execute-assembly-and-mail-slots/
极简的代码示例:
https://gist.github.com/xpn/e95a62c6afcf06ede52568fcd8187cc2
https://modexp.wordpress.com/2019/05/10/dotnet-loader-shellcode/
https://github.com/med0x2e/ExecuteAssembly
https://github.com/Accenture/CLRvoyance
xxxxxxxxxxnamespace SharpSploit.Execution{ public class Assembly { public static void AssemblyExecute(byte[] AssemblyBytes, Object[] Args = null) { if (Args == null) { Args = new Object[] { new string[] { } }; } Reflect.Assembly assembly = Load(AssemblyBytes); assembly.EntryPoint.Invoke(null, Args); } public static GenericObjectResult AssemblyExecute(byte[] AssemblyBytes, String TypeName = "", String MethodName = "", Object[] Parameters = default(Object[])) { Reflect.Assembly assembly = Load(AssemblyBytes); Type type = TypeName == "" ? assembly.GetTypes()[0] : assembly.GetType(TypeName); Reflect.MethodInfo method = MethodName == "" ? type.GetMethods()[0] : type.GetMethod(MethodName); var results = method.Invoke(null, Parameters); return new GenericObjectResult(results); } }}xxxxxxxxxxBOOL LoadAssembly(PDONUT_INSTANCE inst, PDONUT_MODULE mod, PDONUT_ASSEMBLY pa) { HRESULT hr = S_OK; BSTR domain; SAFEARRAYBOUND sab; SAFEARRAY *sa; DWORD i; BOOL loaded=FALSE, loadable; PBYTE p; WCHAR buf[DONUT_MAX_NAME]; if(inst->api.CLRCreateInstance != NULL) { DPRINT("CLRCreateInstance"); hr = inst->api.CLRCreateInstance( (REFCLSID)&inst->xCLSID_CLRMetaHost, (REFIID)&inst->xIID_ICLRMetaHost, (LPVOID*)&pa->icmh); if(SUCCEEDED(hr)) { DPRINT("ICLRMetaHost::GetRuntime(\"%s\")", mod->runtime); ansi2unicode(inst, mod->runtime, buf); hr = pa->icmh->lpVtbl->GetRuntime( pa->icmh, buf, (REFIID)&inst->xIID_ICLRRuntimeInfo, (LPVOID)&pa->icri); if(SUCCEEDED(hr)) { DPRINT("ICLRRuntimeInfo::IsLoadable"); hr = pa->icri->lpVtbl->IsLoadable(pa->icri, &loadable); if(SUCCEEDED(hr) && loadable) { DPRINT("ICLRRuntimeInfo::GetInterface"); hr = pa->icri->lpVtbl->GetInterface( pa->icri, (REFCLSID)&inst->xCLSID_CorRuntimeHost, (REFIID)&inst->xIID_ICorRuntimeHost, (LPVOID)&pa->icrh); DPRINT("HRESULT: %08lx", hr); } } else pa->icri = NULL; } else pa->icmh = NULL; }
if(FAILED(hr)) { DPRINT("CLRCreateInstance failed. Trying CorBindToRuntime"); hr = inst->api.CorBindToRuntime( NULL, // load whatever's available NULL, // load workstation build &inst->xCLSID_CorRuntimeHost, &inst->xIID_ICorRuntimeHost, (LPVOID*)&pa->icrh); DPRINT("HRESULT: %08lx", hr); } if(FAILED(hr)) { pa->icrh = NULL; return FALSE; } DPRINT("ICorRuntimeHost::Start"); hr = pa->icrh->lpVtbl->Start(pa->icrh); if(SUCCEEDED(hr)) { DPRINT("Domain is %s", mod->domain); ansi2unicode(inst, mod->domain, buf); domain = inst->api.SysAllocString(buf); DPRINT("ICorRuntimeHost::CreateDomain(\"%ws\")", buf); hr = pa->icrh->lpVtbl->CreateDomain( pa->icrh, domain, NULL, &pa->iu); inst->api.SysFreeString(domain); if(SUCCEEDED(hr)) { DPRINT("IUnknown::QueryInterface"); hr = pa->iu->lpVtbl->QueryInterface( pa->iu, (REFIID)&inst->xIID_AppDomain, (LPVOID)&pa->ad); if(SUCCEEDED(hr)) { sab.lLbound = 0; sab.cElements = mod->len; sa = inst->api.SafeArrayCreate(VT_UI1, 1, &sab); if(sa != NULL) { DPRINT("Copying %" PRIi32 " bytes of assembly to safe array", mod->len); for(i=0, p=sa->pvData; i<mod->len; i++) { p[i] = mod->data[i]; } DPRINT("AppDomain::Load_3"); hr = pa->ad->lpVtbl->Load_3( pa->ad, sa, &pa->as); loaded = hr == S_OK; DPRINT("HRESULT : %08lx", hr); DPRINT("Erasing assembly from memory"); for(i=0, p=sa->pvData; i<mod->len; i++) { p[i] = mod->data[i] = 0; } DPRINT("SafeArrayDestroy"); inst->api.SafeArrayDestroy(sa); } } } } return loaded;}
BOOL RunAssembly(PDONUT_INSTANCE inst, PDONUT_MODULE mod, PDONUT_ASSEMBLY pa) { SAFEARRAY *sav=NULL, *args=NULL; VARIANT arg, ret, vtPsa, v1={0}, v2; DWORD i; HRESULT hr; BSTR cls, method; ULONG cnt; OLECHAR str[1]={0}; LONG ucnt, lcnt; WCHAR **argv, buf[DONUT_MAX_NAME+1]; int argc; DPRINT("Type is %s", mod->type == DONUT_MODULE_NET_DLL ? "DLL" : "EXE"); // if this is a program if(mod->type == DONUT_MODULE_NET_EXE) { // get the entrypoint DPRINT("MethodInfo::EntryPoint"); hr = pa->as->lpVtbl->EntryPoint(pa->as, &pa->mi); if(SUCCEEDED(hr)) { // get the parameters for entrypoint DPRINT("MethodInfo::GetParameters"); hr = pa->mi->lpVtbl->GetParameters(pa->mi, &args); if(SUCCEEDED(hr)) { DPRINT("SafeArrayGetLBound"); hr = inst->api.SafeArrayGetLBound(args, 1, &lcnt); DPRINT("SafeArrayGetUBound"); hr = inst->api.SafeArrayGetUBound(args, 1, &ucnt); cnt = ucnt - lcnt + 1; DPRINT("Number of parameters for entrypoint : %i", cnt); // does Main require string[] args? if(cnt != 0) { // create a 1 dimensional array for Main parameters sav = inst->api.SafeArrayCreateVector(VT_VARIANT, 0, 1); // if user specified their own parameters, add to string array if(mod->param[0] != 0) { ansi2unicode(inst, mod->param, buf); argv = inst->api.CommandLineToArgvW(buf, &argc); // create 1 dimensional array for strings[] args vtPsa.vt = (VT_ARRAY | VT_BSTR); vtPsa.parray = inst->api.SafeArrayCreateVector(VT_BSTR, 0, argc); // add each string parameter for(i=0; i<argc; i++) { DPRINT("Adding \"%ws\" as parameter %i", argv[i], (i + 1)); inst->api.SafeArrayPutElement(vtPsa.parray, &i, inst->api.SysAllocString(argv[i])); } } else { DPRINT("Adding empty string for invoke_3"); // add empty string to make it work // create 1 dimensional array for strings[] args vtPsa.vt = (VT_ARRAY | VT_BSTR); vtPsa.parray = inst->api.SafeArrayCreateVector(VT_BSTR, 0, 1); i=0; inst->api.SafeArrayPutElement(vtPsa.parray, &i, inst->api.SysAllocString(str)); } // add string array to list of parameters i=0; inst->api.SafeArrayPutElement(sav, &i, &vtPsa); } v1.vt = VT_NULL; v1.plVal = NULL; DPRINT("MethodInfo::Invoke_3()\n"); hr = pa->mi->lpVtbl->Invoke_3(pa->mi, v1, sav, &v2); DPRINT("MethodInfo::Invoke_3 : %08lx : %s", hr, SUCCEEDED(hr) ? "Success" : "Failed"); if(sav != NULL) { inst->api.SafeArrayDestroy(vtPsa.parray); inst->api.SafeArrayDestroy(sav); } } } else pa->mi = NULL; } else { ansi2unicode(inst, mod->cls, buf); cls = inst->api.SysAllocString(buf); if(cls == NULL) return FALSE; DPRINT("Class: SysAllocString(\"%ws\")", buf); ansi2unicode(inst, mod->method, buf); method = inst->api.SysAllocString(buf); DPRINT("Method: SysAllocString(\"%ws\")", buf); if(method != NULL) { DPRINT("Assembly::GetType_2"); hr = pa->as->lpVtbl->GetType_2(pa->as, cls, &pa->type); if(SUCCEEDED(hr)) { sav = NULL; DPRINT("Parameters: %s", mod->param); if(mod->param[0] != 0) { ansi2unicode(inst, mod->param, buf); argv = inst->api.CommandLineToArgvW(buf, &argc); DPRINT("SafeArrayCreateVector(%li argument(s))", argc); sav = inst->api.SafeArrayCreateVector(VT_VARIANT, 0, argc); if(sav != NULL) { for(i=0; i<argc; i++) { DPRINT("Adding \"%ws\" as argument %i", argv[i], (i+1)); V_BSTR(&arg) = inst->api.SysAllocString(argv[i]); V_VT(&arg) = VT_BSTR; hr = inst->api.SafeArrayPutElement(sav, &i, &arg); if(FAILED(hr)) { DPRINT("SafeArrayPutElement failed."); inst->api.SafeArrayDestroy(sav); sav = NULL; } } } } if(SUCCEEDED(hr)) { DPRINT("Calling Type::InvokeMember_3"); hr = pa->type->lpVtbl->InvokeMember_3( pa->type, method, // name of method BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public, NULL, v1, // empty VARIANT sav, // arguments to method &ret); // return code from method DPRINT("Type::InvokeMember_3 : %08lx : %s", hr, SUCCEEDED(hr) ? "Success" : "Failed"); if(sav != NULL) { inst->api.SafeArrayDestroy(sav); } } } inst->api.SysFreeString(method); } inst->api.SysFreeString(cls); } return TRUE;}