Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/base/sessions/ldap.rb
19778 views
1
# -*- coding: binary -*-
2
3
require 'rex/post/ldap'
4
5
class Msf::Sessions::LDAP
6
#
7
# This interface supports basic interaction.
8
#
9
include Msf::Session::Basic
10
include Msf::Sessions::Scriptable
11
12
# @return [Rex::Post::LDAP::Ui::Console] The interactive console
13
attr_accessor :console
14
# @return [Rex::Proto::LDAP::Client] The LDAP client
15
attr_accessor :client
16
17
attr_accessor :keep_alive_thread
18
19
# @return [Integer] Seconds between keepalive requests
20
attr_accessor :keepalive_seconds
21
22
attr_accessor :platform, :arch
23
attr_reader :framework
24
25
# @param[Rex::IO::Stream] rstream
26
# @param [Hash] opts
27
# @option opts [Rex::Proto::LDAP::Client] :client
28
# @option opts [Integer] :keepalive
29
def initialize(rstream, opts = {})
30
@client = opts.fetch(:client)
31
@keepalive_seconds = opts.fetch(:keepalive_seconds)
32
self.console = Rex::Post::LDAP::Ui::Console.new(self)
33
super(rstream, opts)
34
end
35
36
def cleanup
37
stop_keep_alive_loop
38
super
39
end
40
41
def bootstrap(datastore = {}, handler = nil)
42
session = self
43
session.init_ui(user_input, user_output)
44
45
username = datastore['USERNAME']
46
if username.blank?
47
begin
48
whoami = client.ldapwhoami
49
rescue Net::LDAP::Error => e
50
ilog('ldap session opened with no username and the target does not support the LDAP whoami extension')
51
else
52
username = whoami.delete_prefix('u:').split('\\').last
53
end
54
end
55
@info = "LDAP #{username} @ #{@peer_info}"
56
end
57
58
def execute_file(full_path, args)
59
if File.extname(full_path) == '.rb'
60
Rex::Script::Shell.new(self, full_path).run(args)
61
else
62
console.load_resource(full_path)
63
end
64
end
65
66
def process_autoruns(datastore)
67
['InitialAutoRunScript', 'AutoRunScript'].each do |key|
68
next if datastore[key].nil? || datastore[key].empty?
69
70
args = Shellwords.shellwords(datastore[key])
71
print_status("Session ID #{sid} (#{tunnel_to_s}) processing #{key} '#{datastore[key]}'")
72
execute_script(args.shift, *args)
73
end
74
end
75
76
def type
77
self.class.type
78
end
79
80
# Returns the type of session.
81
#
82
def self.type
83
'ldap'
84
end
85
86
def self.can_cleanup_files
87
false
88
end
89
90
#
91
# Returns the session description.
92
#
93
def desc
94
'LDAP'
95
end
96
97
def address
98
@address ||= client.peerhost
99
end
100
101
def port
102
@port ||= client.peerport
103
end
104
105
##
106
# :category: Msf::Session::Interactive implementors
107
#
108
# Initializes the console's I/O handles.
109
#
110
def init_ui(input, output)
111
self.user_input = input
112
self.user_output = output
113
console.init_ui(input, output)
114
console.set_log_source(log_source)
115
116
super
117
end
118
119
##
120
# :category: Msf::Session::Interactive implementors
121
#
122
# Resets the console's I/O handles.
123
#
124
def reset_ui
125
console.unset_log_source
126
console.reset_ui
127
end
128
129
def exit
130
console.stop
131
end
132
133
##
134
# :category: Msf::Session::Interactive implementors
135
#
136
# Override the basic session interaction to use shell_read and
137
# shell_write instead of operating on rstream directly.
138
def _interact
139
framework.events.on_session_interact(self)
140
framework.history_manager.with_context(name: type.to_sym) do
141
_interact_stream
142
end
143
end
144
145
##
146
# :category: Msf::Session::Interactive implementors
147
#
148
def _interact_stream
149
framework.events.on_session_interact(self)
150
151
console.framework = framework
152
# Call the console interaction of the ldap client and
153
# pass it a block that returns whether or not we should still be
154
# interacting. This will allow the shell to abort if interaction is
155
# canceled.
156
console.interact { interacting != true }
157
console.framework = nil
158
159
# If the stop flag has been set, then that means the user exited. Raise
160
# the EOFError so we can drop this handle like a bad habit.
161
raise EOFError if (console.stopped? == true)
162
end
163
164
def on_registered
165
start_keep_alive_loop
166
end
167
168
# Start a background thread for regularly sending a no-op command to keep the connection alive
169
def start_keep_alive_loop
170
self.keep_alive_thread = framework.threads.spawn("LDAP-shell-keepalive-#{sid}", false) do
171
loop do
172
if client.last_interaction.nil?
173
remaining_sleep = @keepalive_seconds
174
else
175
remaining_sleep = @keepalive_seconds - (Process.clock_gettime(Process::CLOCK_MONOTONIC) - client.last_interaction)
176
end
177
sleep(remaining_sleep)
178
if (Process.clock_gettime(Process::CLOCK_MONOTONIC) - client.last_interaction) > @keepalive_seconds
179
client.search_root_dse
180
end
181
# This should have moved last_interaction forwards
182
fail if (Process.clock_gettime(Process::CLOCK_MONOTONIC) - client.last_interaction) > @keepalive_seconds
183
end
184
end
185
end
186
187
# Stop the background thread
188
def stop_keep_alive_loop
189
keep_alive_thread.kill
190
end
191
end
192
193