From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- nselib/data/psexec/nmap_service.c | 380 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 nselib/data/psexec/nmap_service.c (limited to 'nselib/data/psexec/nmap_service.c') diff --git a/nselib/data/psexec/nmap_service.c b/nselib/data/psexec/nmap_service.c new file mode 100644 index 0000000..a6a563b --- /dev/null +++ b/nselib/data/psexec/nmap_service.c @@ -0,0 +1,380 @@ +/**This is the program that's uploaded to a Windows machine when psexec is run. It acts as a Windows + * service, since that's what Windows expects. When it is started, it's passed a list of programs to + * run. These programs are all expected to be at the indicated path (whether they were uploaded or + * they were always present makes no difference). + * + * After running the programs, the output from each of them is ciphered with a simple xor encryption + * (the encryption key is passed as a parameter; because it crosses the wire, it isn't really a + * security feature, more of validation/obfuscation to prevent sniffers from grabbing the output. This + * output is placed in a temp file. When the cipher is complete, the output is moved into a new file. + * When Nmap detects the presence of this new file, it is downloaded, then all files, temp files, and + * the service (this program) is deleted. + * + * One interesting note is that executable files don't require a specific extension to be used by this + * program. By default, at the time of this writing, Nmap appends a .txt extension to the file. + * + * @args argv[1] The final filename where the ciphered output will go. + * @args argv[2] The temporary file where output is sent before being renamed; this is sent as a parameter + * so we can delete it later (if, say, the script fails). + * @args argv[3] The number of programs that are going to be run. + * @args argv[4] Logging: a boolean value (1 to enable logging, 0 to disable). + * @args argv[5] An 'encryption' key for simple 'xor' encryption. This string can be as long or as short + * as you want, but a longer string will be more secure (although this algorithm should + * *never* really be considered secure). + * @args Remaining There are two arguments for each program to run: a path (including arguments) and + * environmental variables. + * + * @auther Ron Bowes + * @copyright Ron Bowes + * @license "Same as Nmap--See https://nmap.org/book/man-legal.html" + */ + +#include +#include + +FILE *outfile; + +SERVICE_STATUS ServiceStatus; +SERVICE_STATUS_HANDLE hStatus; + +static char *enc_key; +static int enc_key_loc; + +static void log_message(char *format, ...) +{ + static int enabled = 1; + + if(!format) + { + enabled = 0; + DeleteFile("c:\\nmap-log.txt"); + } + + + if(enabled) + { + va_list argp; + FILE *file; + + fopen_s(&file, "c:\\nmap-log.txt", "a"); + + if(file != NULL) + { + va_start(argp, format); + vfprintf(file, format, argp); + va_end(argp); + fprintf(file, "\n"); + fclose(file); + } + } +} + +static char cipher(char c) +{ + if(strlen(enc_key) == 0) + return c; + + c = c ^ enc_key[enc_key_loc]; + enc_key_loc = (enc_key_loc + 1) % strlen(enc_key); + + return c; +} + +static void output(int num, char *str, int length) +{ + int i; + + if(length == -1) + length = strlen(str); + + for(i = 0; i < length; i++) + { + if(str[i] == '\n') + { + fprintf(outfile, "%c", cipher('\n')); + fprintf(outfile, "%c", cipher('0' + (num % 10))); + } + else + { + fprintf(outfile, "%c", cipher(str[i])); + } + } +} + +static void go(int num, char *lpAppPath, char *env, int headless, int include_stderr, char *readfile) +{ + STARTUPINFO startupInfo; + PROCESS_INFORMATION processInformation; + SECURITY_ATTRIBUTES sa; + HANDLE stdout_read, stdout_write; + DWORD creation_flags; + + int bytes_read; + char buffer[1024]; + + /* Create a security attributes structure. This is required to inherit handles. */ + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + + if(!headless) + sa.bInheritHandle = TRUE; + + /* Create a pipe that'll be used for stdout and stderr. */ + if(!headless) + CreatePipe(&stdout_read, &stdout_write, &sa, 1); + + /* Initialize the startup info struct. The most important part is setting the stdout/stderr handle to our pipe. */ + ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); + startupInfo.cb = sizeof(STARTUPINFO); + + if(!headless) + { + startupInfo.dwFlags = STARTF_USESTDHANDLES; + startupInfo.hStdOutput = stdout_write; + if(include_stderr) + startupInfo.hStdError = stdout_write; + } + + /* Log a couple messages. */ + log_message("Attempting to load the program: %s", lpAppPath); + + /* Initialize the PROCESS_INFORMATION structure. */ + ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION)); + + /* To divide the output from one program to the next */ + output(num, "\n", -1); + + /* Decide on the creation flags */ + creation_flags = CREATE_NO_WINDOW; + if(headless) + creation_flags = DETACHED_PROCESS; + + /* Create the actual process with an overly-complicated CreateProcess function. */ + if(!CreateProcess(NULL, lpAppPath, 0, &sa, sa.bInheritHandle, CREATE_NO_WINDOW, env, 0, &startupInfo, &processInformation)) + { + output(num, "Failed to create the process", -1); + + if(!headless) + { + CloseHandle(stdout_write); + CloseHandle(stdout_read); + } + } + else + { + log_message("Successfully created the process!"); + + /* Read the pipe, if it isn't headless */ + if(!headless) + { + /* Close the write handle -- if we don't do this, the ReadFile() coming up gets stuck. */ + CloseHandle(stdout_write); + + /* Read from the pipe. */ + log_message("Attempting to read from the pipe"); + while(ReadFile(stdout_read, buffer, 1023, &bytes_read, NULL)) + { + if(strlen(readfile) == 0) + output(num, buffer, bytes_read); + } + CloseHandle(stdout_read); + + /* If we're reading an output file instead of stdout, do it here. */ + if(strlen(readfile) > 0) + { + FILE *read; + errno_t err; + + log_message("Trying to open output file: %s", readfile); + err = fopen_s(&read, readfile, "rb"); + + if(err) + { + log_message("Couldn't open the readfile: %d", err); + output(num, "Couldn't open the output file", -1); + } + else + { + char buf[1024]; + int count; + + count = fread(buf, 1, 1024, read); + while(count) + { + output(num, buf, count); + count = fread(buf, 1, 1024, read); + } + + fclose(read); + } + } + } + else + { + output(num, "Process has been created", -1); + } + + log_message("Done!"); + } +} + +// Control handler function +static void ControlHandler(DWORD request) +{ + switch(request) + { + case SERVICE_CONTROL_STOP: + + ServiceStatus.dwWin32ExitCode = 0; + ServiceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus (hStatus, &ServiceStatus); + return; + + case SERVICE_CONTROL_SHUTDOWN: + + ServiceStatus.dwWin32ExitCode = 0; + ServiceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus (hStatus, &ServiceStatus); + return; + + default: + break; + } + + SetServiceStatus(hStatus, &ServiceStatus); +} + + + +static void die(int err) +{ + // Not enough arguments + ServiceStatus.dwCurrentState = SERVICE_STOPPED; + ServiceStatus.dwWin32ExitCode = err; + SetServiceStatus(hStatus, &ServiceStatus); +} + +static void ServiceMain(int argc, char** argv) +{ + char *outfile_name; + char *tempfile_name; + int count; + int logging; + int result; + int i; + char *current_directory; + + /* Make sure we got the minimum number of arguments. */ + if(argc < 6) + return; + + /* Read the arguments. */ + outfile_name = argv[1]; + tempfile_name = argv[2]; + count = atoi(argv[3]); + logging = atoi(argv[4]); + enc_key = argv[5]; + enc_key_loc = 0; + current_directory = argv[6]; + + /* If they didn't turn on logging, disable it. */ + if(logging != 1) + log_message(NULL); + + /* Log the state. */ + log_message(""); + log_message("-----------------------"); + log_message("STARTING"); + + /* Log all the arguments. */ + log_message("Arguments: %d\n", argc); + for(i = 0; i < argc; i++) + log_message("Argument %d: %s", i, argv[i]); + + /* Set up the service. Likely unnecessary for what we're doing, but it doesn't hurt. */ + ServiceStatus.dwServiceType = SERVICE_WIN32; + ServiceStatus.dwCurrentState = SERVICE_RUNNING; + ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + ServiceStatus.dwWin32ExitCode = 0; + ServiceStatus.dwServiceSpecificExitCode = 0; + ServiceStatus.dwCheckPoint = 0; + ServiceStatus.dwWaitHint = 0; + hStatus = RegisterServiceCtrlHandler("", (LPHANDLER_FUNCTION)ControlHandler); + SetServiceStatus(hStatus, &ServiceStatus); + + /* Registering Control Handler failed (this is a bit late, but eh?) */ + if(hStatus == (SERVICE_STATUS_HANDLE)0) + { + log_message("Service failed to start"); + die(-1); + return; + } + + /* Set the current directory. */ + SetCurrentDirectory(current_directory); + + /* Open the output file. */ + log_message("Opening temporary output file: %s", tempfile_name); + + /* Open the outfile. */ + if(result = fopen_s(&outfile, tempfile_name, "wb")) + { + log_message("Couldn't open output file: %d", result); + die(-1); + } + else + { + /* Run the programs we were given. */ + for(i = 0; i < count; i++) + { + char *program = argv[(i*5) + 7]; + char *env = argv[(i*5) + 8]; + char *headless = argv[(i*5) + 9]; + char *include_stderr = argv[(i*5) + 10]; + char *read_file = argv[(i*5) + 11]; + + go(i, program, env, !strcmp(headless, "true"), !strcmp(include_stderr, "true"), read_file); + } + + /* Close the output file. */ + if(fclose(outfile)) + log_message("Couldn't close the file: %d", errno); + + /* Rename the output file (this is what tells Nmap we're done. */ + log_message("Renaming file %s => %s", tempfile_name, outfile_name); + + /* I noticed that sometimes, programs inherit the handle to the file (or something), so I can't change it right + * away. For this reason, allow about 10 seconds to move it. */ + for(i = 0; i < 10; i++) + { + if(rename(tempfile_name, outfile_name)) + { + log_message("Couldn't rename file: %d (will try %d more times)", errno, 10 - i - 1); + } + else + { + log_message("File successfully renamed!"); + break; + } + + Sleep(1000); + } + + /* Clean up and stop the service. */ + die(0); + } +} + +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY ServiceTable[2]; + ServiceTable[0].lpServiceName = ""; + ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; + + ServiceTable[1].lpServiceName = NULL; + ServiceTable[1].lpServiceProc = NULL; + // Start the control dispatcher thread for our service + StartServiceCtrlDispatcher(ServiceTable); +} + -- cgit v1.2.3