Skip to content

Extension Encryption - #791

Open
xHector1337 wants to merge 34 commits into
rapid7:masterfrom
xHector1337:add-extension-encryption-manager
Open

Extension Encryption #791
xHector1337 wants to merge 34 commits into
rapid7:masterfrom
xHector1337:add-extension-encryption-manager

Conversation

@xHector1337

@xHector1337 xHector1337 commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Extension Encryption: Encryption Manager

This PR creates the basic encryption manager, and start wiring decryption logic inside metsrv.

Coverage:

  • Initialization of Extension Encryption Manager inside the server setup
  • Insertion of an extension if flagged as ENCRYPTABLE and if it was load by ReflectiveLoader
  • Decryption of extension if command handler is within extension mem space
  • Decryption of scheduler event handler if handler is within extension mem space

At 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 ENCRYPTABLE flag set to true will be used as first PoC to ensure writing the memory will not cause any issue for extension loaded with ReflectiveLoader.

@xHector1337
xHector1337 force-pushed the add-extension-encryption-manager branch from b7248da to df37b63 Compare February 4, 2026 14:39
Comment thread c/meterpreter/source/metsrv/scheduler.c Outdated
Comment thread c/meterpreter/source/metsrv/extension_encryption.h
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
@dledda-r7

Copy link
Copy Markdown
Contributor

Side note for future me: BEFORE LANDING

  • Pass every function to use the win api

…tion/manager-2

Fix base address, refactor add function
@xHector1337

Copy link
Copy Markdown
Contributor Author
[2500] [LOAD EXTENSION] Adding command ID to response: 1072
[2500] [LOAD EXTENSION] Adding command ID to response: 1071
[2500] [LOAD EXTENSION] Adding command ID to response: 1077
[2500] [LOAD EXTENSION] Adding command ID to response: 1069
[2500] [LOAD EXTENSION] Adding command ID to response: 1068
[2500] [LOAD EXTENSION] Adding command ID to response: 1067
[2500] [LOAD EXTENSION] Adding command ID to response: 1012
[2500] [LOAD EXTENSION] Adding command ID to response: 1013
[2500] [LOAD EXTENSION] Adding command ID to response: 1015
[2500] [LOAD EXTENSION] Adding command ID to response: 1010
[2500] [LOAD EXTENSION] Adding command ID to response: 1005
[2500] [LOAD EXTENSION] Adding command ID to response: 1007
[2500] [LOAD EXTENSION] Adding command ID to response: 1006
[2500] [LOAD EXTENSION] Adding command ID to response: 1016
[2500] [LOAD EXTENSION] Adding command ID to response: 1014
[2500] [LOAD EXTENSION] Adding command ID to response: 1004
[2500] [LOAD EXTENSION] Adding command ID to response: 1003
[2500] [LOAD EXTENSION] Adding command ID to response: 1011
[2500] [LOAD EXTENSION] Adding command ID to response: 1001
[2500] [LOAD EXTENSION] Adding command ID to response: 1008
[2500] [LOAD EXTENSION] Adding command ID to response: 1009
[2500] [LOAD EXTENSION] Adding command ID to response: 1031
[2500] [LOAD EXTENSION] Adding command ID to response: 1030
[2500] [LOAD EXTENSION] Adding command ID to response: 1029
[2500] [LOAD EXTENSION] Adding command ID to response: 1028
[2500] [LOAD EXTENSION] Adding command ID to response: 4
[2500] [extension_encryption][extension_encryption_add] Adding extension
[2500] [extension_encryption][extension_encryption_add] .text section of the extension is found!
[2500] [extension_encryption][extension_encryption_add] lpExtensionStatus->bEncryptable: 1
[2500] [extension_encryption][extension_encryption_add] lpExtensionStatus->bEncrypted: 0
[2500] [extension_encryption][extension_encryption_add] lpExtensionStatus->lpLoc: 00000000008D1000
[2500] [extension_encryption][extension_encryption_add] lpExtensionStatus->dwSize: 323376
[2500] [extension_encryption][extension_encryption_add] lpExtensionStatus->dwLastUsedTime: 326078
[2500] [extension_encryption][extension_encryption_add] Added extension text section at 00000000008D1000 of size 323376
[2500] [extension_encryption][extension_encryption_add] Function exiting

DebugString: "[27e8] [DISPATCH] Packet type for 1072 is 0"
DebugString: "[27e8] [DISPATCH] executing request handler 1072"
DebugString: "[27e8] [DISPATCH] Calling extensionFindDecrypt for command 1072"
DebugString: "[27e8] [extension_encryption][extension_encryption_get] Getting extension."
DebugString: "[27e8] [extension_encryption][extension_encryption_get] Function exiting."
DebugString: "[27e8] [COMMAND] Decryption successful for command 1072"

@xHector1337
xHector1337 marked this pull request as ready for review March 22, 2026 06:46
@dledda-r7
dledda-r7 requested a review from Copilot April 16, 2026 15:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +326 to 348
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);

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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;
}

Copilot uses AI. Check for mistakes.
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
Comment thread c/meterpreter/source/metsrv/scheduler.c Outdated
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
Comment thread c/meterpreter/source/metsrv/extension_encryption.c Outdated
Comment thread c/meterpreter/source/metsrv/base.c
Comment thread c/meterpreter/source/metsrv/server_setup.c Outdated
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
Comment thread c/meterpreter/source/metsrv/extension_encryption.c

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 FlushInstructionCache for 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_extension call failed and ignores add failure; Impact: failed extensions can enter the encryption scan, while successfully loaded encryptable extensions can be reported as successful without ever being protected; Fix: call add only after res == 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);
				}

Comment thread c/meterpreter/source/metsrv/extension_encryption.c
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
Comment thread c/meterpreter/source/metsrv/extension_encryption.c
…ction

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Comment thread c/meterpreter/source/metsrv/scheduler.c
Comment thread c/meterpreter/source/metsrv/scheduler.c Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants