CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/handler.rb
Views: 1904
1
# -*- coding: binary -*-
2
3
module Msf
4
5
###
6
#
7
# This module acts as a base for all handler pseudo-modules. They aren't
8
# really modules, so don't get the wrong idea champs! They're merely
9
# mixed into dynamically generated payloads to handle monitoring for
10
# a connection. Handlers are layered in between the base payload
11
# class and any other payload class. A super cool ASCII diagram would
12
# look something like this
13
#
14
# Module
15
# ^
16
# |
17
# Payload
18
# ^
19
# |
20
# Handler
21
# ^
22
# |
23
# Stager
24
# ^
25
# |
26
# Stage
27
#
28
###
29
module Handler
30
31
##
32
#
33
# Constants used with the ``handler'' method to indicate whether or not the
34
# connection was used.
35
#
36
##
37
38
#
39
# Returned by handlers to indicate that a socket has been claimed for use
40
# by the payload.
41
#
42
Claimed = "claimed"
43
#
44
# Returned by handlers to indicate that a socket has not been claimed for
45
# use.
46
#
47
Unused = "unused"
48
49
#
50
# Returns the handler type.
51
#
52
def self.handler_type
53
return "none"
54
end
55
56
#
57
# Returns the transport-independent handler type.
58
#
59
def self.general_handler_type
60
"none"
61
end
62
63
#
64
# Returns the handler's name, if any.
65
#
66
def handler_name
67
module_info['HandlerName']
68
end
69
70
#
71
# Initializes the session waiter event and other fun stuff.
72
#
73
def initialize(info = {})
74
super
75
76
# Initialize the pending_connections counter to 0
77
self.pending_connections = 0
78
79
# Initialize the sessions counter to 0
80
self.sessions = 0
81
82
# Create the waiter event with auto_reset set to false so that
83
# if a session is ever created, waiting on it returns immediately.
84
self.session_waiter_event = Rex::Sync::Event.new(false, false)
85
end
86
87
#
88
# Sets up the connection handler.
89
#
90
def setup_handler
91
end
92
93
#
94
# Terminates the connection handler.
95
#
96
def cleanup_handler
97
end
98
99
#
100
# Start monitoring for a connection.
101
#
102
def start_handler
103
end
104
105
#
106
# Start another connection monitor
107
#
108
def add_handler(opts={})
109
end
110
111
#
112
# Stop monitoring for a connection.
113
#
114
def stop_handler
115
end
116
117
#
118
# Checks to see if a payload connection has been established on
119
# the supplied connection. This is necessary for find-sock style
120
# payloads.
121
#
122
def handler(sock)
123
end
124
125
#
126
# Handles an established connection supplied in the in and out
127
# handles. The handles are passed as parameters in case this
128
# handler is capable of handling multiple simultaneous
129
# connections. The default behavior is to attempt to create a session for
130
# the payload. This path will not be taken for multi-staged payloads.
131
#
132
def handle_connection(conn, opts={})
133
create_session(conn, opts)
134
end
135
136
#
137
# The amount of time to wait for a session to come in.
138
#
139
def wfs_delay
140
2
141
end
142
143
#
144
# Waits for a session to be created as the result of a handler connection
145
# coming in. The return value is a session object instance on success or
146
# nil if the timeout expires.
147
#
148
def wait_for_session(t = wfs_delay)
149
session = nil
150
151
begin
152
session = session_waiter_event.wait(t)
153
rescue ::Timeout::Error
154
end
155
156
# If a connection has arrived, wait longer...
157
if (pending_connections > 0)
158
session = session_waiter_event.wait
159
end
160
161
return session
162
end
163
164
#
165
# Interrupts a wait_for_session call by notifying with a nil event
166
#
167
def interrupt_wait_for_session
168
return unless session_waiter_event
169
session_waiter_event.notify(nil)
170
end
171
172
#
173
# Set by the exploit module to configure handler
174
#
175
attr_accessor :exploit_config
176
177
#
178
# This will be non-nil if the handler has a parent payload that it
179
# was spawned from. Right now, this is only the case with generic
180
# payloads. The parent payload is used to create a session
181
# rather than using the instance itself.
182
#
183
attr_accessor :parent_payload
184
185
protected
186
187
#
188
# Creates a session, if necessary, for the connection that's been handled.
189
# Sessions are only created if the payload that's been mixed in has an
190
# associated session.
191
#
192
def create_session(conn, opts={})
193
# If there is a parent payload, then use that in preference.
194
return parent_payload.create_session(conn, opts) if (parent_payload)
195
196
# If the payload we merged in with has an associated session factory,
197
# allocate a new session.
198
if (self.session)
199
begin
200
# if there's a create_session method then use it, as this
201
# can form a factory for arb session types based on the
202
# payload.
203
if self.session.respond_to?('create_session')
204
s = self.session.create_session(conn, opts)
205
else
206
s = self.session.new(conn, opts)
207
end
208
rescue ::Exception => e
209
# We just wanna show and log the error, not trying to swallow it.
210
print_error("#{e.class} #{e.message}")
211
elog('Could not allocate a new Session.', error: e)
212
raise e
213
end
214
215
# Pass along the framework context
216
s.framework = framework
217
218
# Associate this system with the original exploit
219
# and any relevant information
220
s.set_from_exploit(assoc_exploit)
221
222
# set injected workspace value if db is active
223
if framework.db.active && wspace = framework.db.find_workspace(s.workspace)
224
framework.db.workspace = wspace
225
end
226
227
# Pass along any associated payload uuid if specified
228
if opts[:payload_uuid]
229
s.payload_uuid = opts[:payload_uuid]
230
s.payload_uuid.registered = false
231
if framework.db.active
232
payload_info = { uuid: s.payload_uuid.puid_hex, workspace: framework.db.workspace }
233
uuid_info = framework.db.payloads(payload_info).first
234
else
235
print_warning('Without a database connected that payload UUID tracking will not work!')
236
end
237
if s.payload_uuid.respond_to?(:puid_hex) && uuid_info
238
s.payload_uuid.registered = true
239
s.payload_uuid.name = uuid_info['name']
240
s.payload_uuid.timestamp = uuid_info['timestamp']
241
else
242
s.payload_uuid.registered = false
243
end
244
end
245
246
# If the session is valid, register it with the framework and
247
# notify any waiters we may have.
248
if (s)
249
# Defer the session registration to the Session Manager scheduler
250
registration = Proc.new do
251
register_session(s)
252
end
253
framework.sessions.schedule registration
254
end
255
256
return s
257
end
258
nil
259
end
260
261
#
262
# Registers a session with the framework and notifies any waiters of the
263
# new session.
264
#
265
def register_session(session)
266
# Register the session with the framework
267
framework.sessions.register(session)
268
269
# Call the handler's on_session() method
270
if session.respond_to?(:bootstrap)
271
session.bootstrap(datastore, self)
272
273
return unless session.alive
274
end
275
276
# Process the auto-run scripts for this session
277
if session.respond_to?(:process_autoruns)
278
session.process_autoruns(datastore)
279
end
280
281
# Tell the handler that we have a session
282
on_session(session)
283
284
# Notify the framework that we have a new session opening up...
285
# Don't let errant event handlers kill our session
286
begin
287
framework.events.on_session_open(session)
288
rescue ::Exception => e
289
wlog("Exception in on_session_open event handler: #{e.class}: #{e}")
290
wlog("Call Stack\n#{e.backtrace.join("\n")}")
291
end
292
293
# If there is an exploit associated with this payload, then let's notify
294
# anyone who is interested that this exploit succeeded
295
if assoc_exploit
296
framework.events.on_exploit_success(assoc_exploit, session)
297
end
298
299
# Notify waiters that they should be ready to rock
300
session_waiter_event.notify(session)
301
302
# Decrement the pending connections counter now that we've processed
303
# one session.
304
self.pending_connections -= 1
305
306
# Count the number of sessions we have registered
307
self.sessions += 1
308
end
309
310
attr_accessor :session_waiter_event # :nodoc:
311
attr_accessor :pending_connections # :nodoc:
312
attr_accessor :sessions # :nodoc:
313
314
end
315
316
end
317
318