Extension Encryption - #791
Conversation
b7248da to
df37b63
Compare
…extension-encryption/manager
…tion/manager Collaboration Extension Encryption
…the scheduler and command_process_inline
…actor add function
|
Side note for future me: BEFORE LANDING
|
…tion/manager-2 Fix base address, refactor add function
|
There was a problem hiding this comment.
Pull request overview
Adds an “Extension Encryption Manager” to metsrv and wires it into extension load and dispatch paths so extensions flagged as encryptable can be tracked and (de)crypted around handler execution.
Changes:
- Add
extension_encryption.{c,h}implementing a global extension encryption manager (debug + RC4 managers) and routines to add/find/(de)crypt extensions. - Initialize the manager during server setup; register encryptable, reflectively-loaded extensions during
core_loadlib. - Attempt decryption before running extension handlers in the command dispatcher and scheduler; periodically encrypt unused extensions.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| c/meterpreter/workspace/metsrv/metsrv.vcxproj | Adds new extension encryption source/header to the build. |
| c/meterpreter/workspace/metsrv/metsrv.vcxproj.filters | Adds new files to Visual Studio filters. |
| c/meterpreter/source/metsrv/server_setup.c | Initializes the Extension Encryption Manager during server setup. |
| c/meterpreter/source/metsrv/remote_dispatch.c | Registers encryptable, reflectively-loaded extensions with the manager. |
| c/meterpreter/source/metsrv/base.c | Attempts extension decryption before handler execution; triggers “encrypt unused” after command handling. |
| c/meterpreter/source/metsrv/scheduler.c | Attempts extension decryption before scheduled routine execution. |
| c/meterpreter/source/metsrv/extension_encryption.h | Declares manager/status structs and public APIs. |
| c/meterpreter/source/metsrv/extension_encryption.c | Implements manager initialization, extension tracking, and (de)cryption routines. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!extensionFindDecryptVal || extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_ENCRYPTABLE) { | ||
| dprintf("[COMMAND] Decryption successful for command %u", commandId); | ||
| } | ||
| else { | ||
| dprintf("[COMMAND] Decryption failed for command %u", commandId); | ||
| //break; | ||
| } | ||
| serverContinue = command->request.inline_handler(remote, packet, &result) && serverContinue; | ||
| dprintf("[DISPATCH] executed %u, continue %s", commandId, serverContinue ? "yes" : "no"); | ||
| } | ||
| else | ||
| { | ||
| dprintf("[DISPATCH] executing request handler %u", commandId); | ||
| dprintf("[DISPATCH] Calling extensionFindDecrypt for command %u", commandId); | ||
| extensionFindDecryptVal = extensionFindDecrypt(command->request.handler); | ||
| if (!extensionFindDecryptVal || extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_ENCRYPTABLE) { | ||
| dprintf("[COMMAND] Decryption successful for command %u", commandId); | ||
| } | ||
| else { | ||
| dprintf("[COMMAND] Decryption failed for command %u", commandId); | ||
| //break; | ||
| } | ||
| result = command->request.handler(remote, packet); |
There was a problem hiding this comment.
If extensionFindDecrypt() reports a decryption failure, the handler is still invoked. For handlers located inside an encrypted extension, this risks executing encrypted bytes (crash/undefined behavior). Consider short-circuiting the dispatch when decryption fails (except for "not encryptable" / "not found" cases), and avoid logging "Decryption successful" unless the return code indicates success.
| if (!extensionFindDecryptVal || extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_ENCRYPTABLE) { | |
| dprintf("[COMMAND] Decryption successful for command %u", commandId); | |
| } | |
| else { | |
| dprintf("[COMMAND] Decryption failed for command %u", commandId); | |
| //break; | |
| } | |
| serverContinue = command->request.inline_handler(remote, packet, &result) && serverContinue; | |
| dprintf("[DISPATCH] executed %u, continue %s", commandId, serverContinue ? "yes" : "no"); | |
| } | |
| else | |
| { | |
| dprintf("[DISPATCH] executing request handler %u", commandId); | |
| dprintf("[DISPATCH] Calling extensionFindDecrypt for command %u", commandId); | |
| extensionFindDecryptVal = extensionFindDecrypt(command->request.handler); | |
| if (!extensionFindDecryptVal || extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_ENCRYPTABLE) { | |
| dprintf("[COMMAND] Decryption successful for command %u", commandId); | |
| } | |
| else { | |
| dprintf("[COMMAND] Decryption failed for command %u", commandId); | |
| //break; | |
| } | |
| result = command->request.handler(remote, packet); | |
| if (!extensionFindDecryptVal) { | |
| dprintf("[COMMAND] Decryption successful for command %u", commandId); | |
| serverContinue = command->request.inline_handler(remote, packet, &result) && serverContinue; | |
| dprintf("[DISPATCH] executed %u, continue %s", commandId, serverContinue ? "yes" : "no"); | |
| } | |
| else if (extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_ENCRYPTABLE | |
| || extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_FOUND) { | |
| dprintf("[COMMAND] Decryption not required for command %u", commandId); | |
| serverContinue = command->request.inline_handler(remote, packet, &result) && serverContinue; | |
| dprintf("[DISPATCH] executed %u, continue %s", commandId, serverContinue ? "yes" : "no"); | |
| } | |
| else { | |
| dprintf("[COMMAND] Decryption failed for command %u with error %u", commandId, extensionFindDecryptVal); | |
| result = extensionFindDecryptVal; | |
| } | |
| } | |
| else | |
| { | |
| dprintf("[DISPATCH] executing request handler %u", commandId); | |
| dprintf("[DISPATCH] Calling extensionFindDecrypt for command %u", commandId); | |
| extensionFindDecryptVal = extensionFindDecrypt(command->request.handler); | |
| if (!extensionFindDecryptVal) { | |
| dprintf("[COMMAND] Decryption successful for command %u", commandId); | |
| result = command->request.handler(remote, packet); | |
| } | |
| else if (extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_ENCRYPTABLE | |
| || extensionFindDecryptVal == EXTENSION_ENCRYPTION_EXTENSION_NOT_FOUND) { | |
| dprintf("[COMMAND] Decryption not required for command %u", commandId); | |
| result = command->request.handler(remote, packet); | |
| } | |
| else { | |
| dprintf("[COMMAND] Decryption failed for command %u with error %u", commandId, extensionFindDecryptVal); | |
| result = extensionFindDecryptVal; | |
| } |
… in extension encryption functions
…in extension encryption, sync rc4 stream
…tion/manager-2 some other fixes
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Suppressed comments (3)
c/meterpreter/source/metsrv/extension_encryption.c:501
- Important: Problem: when the second decryption buffer allocation fails, the read buffer is freed here without clearing its pointer and common cleanup frees it again; Impact: this error path can corrupt the process heap or terminate metsrv; Fix: set the pointer to NULL after the first free.
if (lpTempBufferRead != NULL) {
HeapFree(hHeap, 0, lpTempBufferRead);
}
c/meterpreter/source/metsrv/extension_encryption.c:536
- Critical: Problem: decrypted executable bytes are written and invoked without flushing the instruction cache; Impact: Windows permits the processor to execute stale encrypted instructions, causing intermittent illegal-instruction crashes when a handler runs; Fix: call
FlushInstructionCachefor the rewritten section and fail decryption if it does not succeed before marking the extension decrypted.
ret = met_api->win_api.kernel32.WriteProcessMemory(GetCurrentProcess(), (unsigned char*)ExtensionLoc + i, lpTempBufferWrite, diff, &ByteCounter);
if (!ret || ByteCounter != diff) {
dprintf("[extension_encryption][extension_encryption_decrypt] WriteProcessMemory failed with error 0x%x", GetLastError());
bError = TRUE;
break;
c/meterpreter/source/metsrv/remote_dispatch.c:432
- Important: Problem: this registers the module even when the preceding
load_extensioncall failed and ignoresaddfailure; Impact: failed extensions can enter the encryption scan, while successfully loaded encryptable extensions can be reported as successful without ever being protected; Fix: calladdonly afterres == ERROR_SUCCESS, propagate registration failure, and roll back the loaded extension if encryption registration is mandatory.
if (flags & LOAD_LIBRARY_EXTENSION_ENCRYPTABLE && bLibLoadedReflectivly) {
ExtensionEncryptionManager* encryptionManager = GetExtensionEncryptionManager();
if(encryptionManager) {
encryptionManager->add(lpLibraryLocation);
}
…ction Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Extension Encryption: Encryption Manager
This PR creates the basic encryption manager, and start wiring decryption logic inside metsrv.
Coverage:
ENCRYPTABLEand if it was load by ReflectiveLoaderAt the moment the default CryptographicManager is the a "debug" manager that will just force metsrv to rewrite the same bytes to the extension memory space, this chained with a testing extension loaded with the
ENCRYPTABLEflag set to true will be used as first PoC to ensure writing the memory will not cause any issue for extension loaded with ReflectiveLoader.