Skip to content

Commit 26e7fe1

Browse files
committed
Merge branch 'upstream' into staging/rails-4.0
Conflicts: Gemfile.lock
2 parents 0b608e1 + c540ba4 commit 26e7fe1

File tree

62 files changed

+2608
-614
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2608
-614
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ PATH
99
json
1010
metasploit-concern (= 1.0.0.pre.rails.pre.4.0)
1111
metasploit-model (= 1.0.0.pre.rails.pre.4.0)
12-
meterpreter_bins (= 0.0.22)
12+
metasploit-payloads (= 0.0.3)
1313
msgpack
1414
nokogiri
1515
packetfu (= 1.1.9)
@@ -122,6 +122,7 @@ GEM
122122
metasploit-model (1.0.0.pre.rails.pre.4.0)
123123
activesupport (>= 4.0.9, < 4.1.0)
124124
railties (>= 4.0.9, < 4.1.0)
125+
metasploit-payloads (0.0.3)
125126
metasploit_data_models (1.0.0.pre.rails.pre.4.0b)
126127
activerecord (>= 4.0.9, < 4.1.0)
127128
activesupport (>= 4.0.9, < 4.1.0)
@@ -132,7 +133,6 @@ GEM
132133
postgres_ext
133134
railties (>= 4.0.9, < 4.1.0)
134135
recog (~> 1.0)
135-
meterpreter_bins (0.0.22)
136136
method_source (0.8.2)
137137
mime-types (2.4.3)
138138
mini_portile (0.6.2)
-35.3 KB
Binary file not shown.
-42.6 KB
Binary file not shown.
-204 KB
Binary file not shown.
-1.42 MB
Binary file not shown.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
2+
require 'metasploit/framework/login_scanner/http'
3+
4+
module Metasploit
5+
module Framework
6+
module LoginScanner
7+
8+
class ManageEngineDesktopCentral < HTTP
9+
10+
DEFAULT_PORT = 8020
11+
PRIVATE_TYPES = [ :password ]
12+
LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name
13+
14+
15+
# Checks if the target is ManageEngine Dekstop Central.
16+
#
17+
# @return [Boolean] TrueClass if target is MSP, otherwise FalseClass
18+
def check_setup
19+
login_uri = normalize_uri("#{uri}/configurations.do")
20+
res = send_request({'uri' => login_uri})
21+
22+
if res && res.body.include?('ManageEngine Desktop Central')
23+
return true
24+
end
25+
26+
false
27+
end
28+
29+
30+
# Returns the latest sid from MSP
31+
#
32+
# @param [Rex::Proto::Http::Response]
33+
# @return [String] The session ID for MSP
34+
def get_sid(res)
35+
cookies = res.get_cookies
36+
sid = cookies.scan(/(DCJSESSIONID=\w+);*/).flatten[0] || ''
37+
sid
38+
end
39+
40+
41+
42+
# Returns the hidden inputs
43+
#
44+
# @param [Rex::Proto::Http::Response]
45+
# @return [Hash] Input fields
46+
def get_hidden_inputs(res)
47+
found_inputs = {}
48+
res.body.scan(/(<input type="hidden" .+>)/).flatten.each do |input|
49+
name = input.scan(/name="(\w+)"/).flatten[0] || ''
50+
value = input.scan(/value="([\w\.\-]+)"/).flatten[0] || ''
51+
found_inputs[name] = value
52+
end
53+
found_inputs
54+
end
55+
56+
57+
# Returns all the items needed to login
58+
#
59+
# @return [Hash] Login items
60+
def get_required_login_items
61+
items = {}
62+
login_uri = normalize_uri("#{uri}/configurations.do")
63+
res = send_request({'uri' => login_uri})
64+
return items unless res
65+
items.merge!({'sid' => get_sid(res)})
66+
items.merge!(get_hidden_inputs(res))
67+
items
68+
end
69+
70+
71+
# Actually doing the login. Called by #attempt_login
72+
#
73+
# @param username [String] The username to try
74+
# @param password [String] The password to try
75+
# @return [Hash]
76+
# * :status [Metasploit::Model::Login::Status]
77+
# * :proof [String] the HTTP response body
78+
def get_login_state(username, password)
79+
login_uri = normalize_uri("#{uri}/j_security_check")
80+
login_items = get_required_login_items
81+
82+
res = send_request({
83+
'uri' => login_uri,
84+
'method' => 'POST',
85+
'cookie' => login_items['sid'],
86+
'vars_post' => {
87+
'j_username' => username,
88+
'j_password' => password,
89+
'Button' => 'Sign+in',
90+
'buildNum' => login_items['buildNum'],
91+
'clearCacheBuildNum' => login_items['clearCacheBuildNum']
92+
}
93+
})
94+
95+
unless res
96+
return {:status => LOGIN_STATUS::UNABLE_TO_CONNECT, :proof => res.to_s}
97+
end
98+
99+
if res.code == 302
100+
return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => res.to_s}
101+
end
102+
103+
{:status => LOGIN_STATUS::INCORRECT, :proof => res.to_s}
104+
end
105+
106+
107+
# Attempts to login to MSP.
108+
#
109+
# @param credential [Metasploit::Framework::Credential] The credential object
110+
# @return [Result] A Result object indicating success or failure
111+
def attempt_login(credential)
112+
result_opts = {
113+
credential: credential,
114+
status: LOGIN_STATUS::INCORRECT,
115+
proof: nil,
116+
host: host,
117+
port: port,
118+
protocol: 'tcp'
119+
}
120+
121+
begin
122+
result_opts.merge!(get_login_state(credential.public, credential.private))
123+
rescue ::Rex::ConnectionError => e
124+
# Something went wrong during login. 'e' knows what's up.
125+
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
126+
end
127+
128+
Result.new(result_opts)
129+
end
130+
131+
end
132+
end
133+
end
134+
end
135+

lib/msf/base/serializer/readable_text.rb

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ def self.dump_sessions(framework, opts={})
527527
indent = opts[:indent] || DefaultIndent
528528
col = opts[:col] || DefaultColumnWrap
529529

530+
return dump_sessions_verbose(framework, opts) if verbose
531+
530532
columns =
531533
[
532534
'Id',
@@ -535,9 +537,6 @@ def self.dump_sessions(framework, opts={})
535537
'Connection'
536538
]
537539

538-
columns << 'Via' if verbose
539-
columns << 'PayloadId' if verbose
540-
541540
tbl = Rex::Ui::Text::Table.new(
542541
'Indent' => indent,
543542
'Header' => "Active sessions",
@@ -554,12 +553,7 @@ def self.dump_sessions(framework, opts={})
554553

555554
row = [ session.sid.to_s, session.type.to_s, sinfo, session.tunnel_to_s + " (#{session.session_host})" ]
556555
if session.respond_to? :platform
557-
row[1] += " " + session.platform
558-
end
559-
560-
if verbose
561-
row << session.via_exploit.to_s
562-
row << session.payload_uuid.to_s
556+
row[1] << (" " + session.platform)
563557
end
564558

565559
tbl << row
@@ -568,6 +562,59 @@ def self.dump_sessions(framework, opts={})
568562
return framework.sessions.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n"
569563
end
570564

565+
# Dumps the list of active sessions in verbose mode
566+
#
567+
# @param framework [Msf::Framework] the framework to dump.
568+
# @param opts [Hash] the options to dump with.
569+
# @option opts :session_ids [Array] the list of sessions to dump (no
570+
# effect).
571+
# @return [String] the formatted list of sessions.
572+
def self.dump_sessions_verbose(framework, opts={})
573+
ids = (opts[:session_ids] || framework.sessions.keys).sort
574+
575+
out = "Active sessions\n" +
576+
"===============\n\n"
577+
578+
if framework.sessions.length == 0
579+
out << "No active sessions.\n"
580+
return out
581+
end
582+
583+
framework.sessions.each_sorted do |k|
584+
session = framework.sessions[k]
585+
586+
sess_info = session.info.to_s
587+
sess_id = session.sid.to_s
588+
sess_tunnel = session.tunnel_to_s + " (#{session.session_host})"
589+
sess_via = session.via_exploit.to_s
590+
sess_type = session.type.to_s
591+
sess_uuid = session.payload_uuid.to_s
592+
sess_checkin = "<none>"
593+
sess_machine_id = session.machine_id.to_s
594+
595+
if session.respond_to? :platform
596+
sess_type << (" " + session.platform)
597+
end
598+
599+
if session.respond_to?(:last_checkin) && session.last_checkin
600+
sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}"
601+
end
602+
603+
out << " Session ID: #{sess_id}\n"
604+
out << " Type: #{sess_type}\n"
605+
out << " Info: #{sess_info}\n"
606+
out << " Tunnel: #{sess_tunnel}\n"
607+
out << " Via: #{sess_via}\n"
608+
out << " UUID: #{sess_uuid}\n"
609+
out << " MachineID: #{sess_machine_id}\n"
610+
out << " CheckIn: #{sess_checkin}\n"
611+
out << "\n"
612+
end
613+
614+
out << "\n"
615+
return out
616+
end
617+
571618
# Dumps the list of running jobs.
572619
#
573620
# @param framework [Msf::Framework] the framework.

lib/msf/base/sessions/meterpreter.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,10 @@ def reset_ui
255255
def kill
256256
begin
257257
cleanup_meterpreter
258-
self.sock.close
258+
self.sock.close if self.sock
259259
rescue ::Exception
260260
end
261+
# deregister will actually trigger another cleanup
261262
framework.sessions.deregister(self)
262263
end
263264

@@ -298,6 +299,24 @@ def load_priv()
298299
console.disable_output = original
299300
end
300301

302+
#
303+
# Validate session information by checking for a machine_id response
304+
#
305+
def is_valid_session?(timeout=10)
306+
return true if self.machine_id
307+
308+
begin
309+
self.machine_id = self.core.machine_id(timeout)
310+
return true
311+
rescue ::Rex::Post::Meterpreter::RequestError
312+
# This meterpreter doesn't support core_machine_id
313+
return true
314+
rescue ::Exception => e
315+
dlog("Session #{self.sid} did not respond to validation request #{e.class}: #{e}")
316+
end
317+
false
318+
end
319+
301320
#
302321
# Populate the session information.
303322
#
@@ -448,6 +467,7 @@ def create(param)
448467
attr_accessor :binary_suffix
449468
attr_accessor :console # :nodoc:
450469
attr_accessor :skip_ssl
470+
attr_accessor :skip_cleanup
451471
attr_accessor :target_id
452472

453473
protected

lib/msf/base/sessions/meterpreter_options.rb

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ def initialize(info = {})
1212
register_advanced_options(
1313
[
1414
OptBool.new('AutoLoadStdapi', [true, "Automatically load the Stdapi extension", true]),
15+
OptBool.new('AutoVerifySession', [true, "Automatically verify and drop invalid sessions", true]),
1516
OptString.new('InitialAutoRunScript', [false, "An initial script to run on session creation (before AutoRunScript)", '']),
1617
OptString.new('AutoRunScript', [false, "A script to run automatically on session creation.", '']),
1718
OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]),
1819
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", Rex::Compat.is_windows]),
19-
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"])
20+
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"]),
21+
OptBool.new('StagerCloseListenSocket', [false, "Close the listen socket in the stager", false]),
22+
OptInt.new('SessionRetryTotal', [false, "Number of seconds try reconnecting for on network failure", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_TOTAL]),
23+
OptInt.new('SessionRetryWait', [false, "Number of seconds to wait between reconnect attempts", Rex::Post::Meterpreter::ClientCore::TIMEOUT_RETRY_WAIT]),
24+
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', Rex::Post::Meterpreter::ClientCore::TIMEOUT_SESSION]),
25+
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', Rex::Post::Meterpreter::ClientCore::TIMEOUT_COMMS])
2026
], self.class)
2127
end
2228

@@ -35,43 +41,49 @@ def on_session(session)
3541

3642
session.init_ui(self.user_input, self.user_output)
3743

38-
if (datastore['AutoLoadStdapi'] == true)
44+
valid = true
3945

40-
session.load_stdapi
41-
42-
if datastore['AutoSystemInfo']
43-
session.load_session_info
46+
if datastore['AutoVerifySession'] == true
47+
if not session.is_valid_session?
48+
print_error("Meterpreter session #{session.sid} is not valid and will be closed")
49+
valid = false
4450
end
51+
end
52+
53+
if valid
54+
55+
if datastore['AutoLoadStdapi'] == true
56+
57+
session.load_stdapi
58+
59+
if datastore['AutoSystemInfo']
60+
session.load_session_info
61+
end
4562

46-
=begin
47-
admin = false
48-
begin
49-
::Timeout.timeout(30) do
50-
if session.railgun and session.railgun.shell32.IsUserAnAdmin()["return"] == true
51-
admin = true
52-
session.info += " (ADMIN)"
53-
end
63+
if session.platform =~ /win32|win64/i
64+
session.load_priv rescue nil
5465
end
55-
rescue ::Exception
5666
end
57-
=end
58-
if session.platform =~ /win32|win64/i
59-
session.load_priv rescue nil
67+
68+
if session.platform =~ /android/i
69+
if datastore['AutoLoadAndroid']
70+
session.load_android
71+
end
6072
end
61-
end
6273

63-
if session.platform =~ /android/i
64-
if datastore['AutoLoadAndroid']
65-
session.load_android
74+
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
75+
if (datastore[key].empty? == false)
76+
args = Shellwords.shellwords( datastore[key] )
77+
print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'")
78+
session.execute_script(args.shift, *args)
79+
end
6680
end
6781
end
6882

69-
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
70-
if (datastore[key].empty? == false)
71-
args = Shellwords.shellwords( datastore[key] )
72-
print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'")
73-
session.execute_script(args.shift, *args)
74-
end
83+
# Terminate the session without cleanup if it did not validate
84+
if not valid
85+
session.skip_cleanup = true
86+
session.kill
7587
end
7688

7789
}

0 commit comments

Comments
 (0)