Module: Msf::Handler::BindAwsSsm

Includes:
Msf::Handler, Rex::Proto::Http::WebSocket::AmazonSsm
Defined in:
lib/msf/core/handler/bind_aws_ssm.rb

Defined Under Namespace

Modules: AwsSsmSessionChannelExt Classes: AwsSsmSessionChannel

Constant Summary

Constants included from Msf::Handler

Claimed, Unused

Instance Attribute Summary collapse

Attributes included from Msf::Handler

#exploit_config, #parent_payload, #pending_connections, #session_waiter_event, #sessions

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Msf::Handler

#handle_connection, #handler, #handler_name, #interrupt_wait_for_session, #register_session, #setup_handler, #wait_for_session, #wfs_delay

Methods included from Rex::Proto::Http::WebSocket::AmazonSsm

#connect_ssm_ws

Instance Attribute Details

#conn_threadsObject (protected)

:nodoc:



377
378
379
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 377

def conn_threads
  @conn_threads
end

#listener_pairsObject (protected)

:nodoc:



379
380
381
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 379

def listener_pairs
  @listener_pairs
end

#listener_threadsObject (protected)

:nodoc:



378
379
380
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 378

def listener_threads
  @listener_threads
end

Class Method Details

.general_handler_typeObject

Returns the connection oriented general handler type, in this case bind.



132
133
134
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 132

def self.general_handler_type
  'bind'
end

.handler_typeObject

Returns the handler specific string representation, in this case ‘bind_aws_ssm’.



125
126
127
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 125

def self.handler_type
  return 'bind_aws_ssm'
end

Instance Method Details

#add_handler(opts = {}) ⇒ Object

Starts a new connecting thread



188
189
190
191
192
193
194
195
196
197
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 188

def add_handler(opts={})

  # Merge the updated datastore values
  opts.each_pair do |k,v|
    datastore[k] = v
  end

  # Start a new handler
  start_handler
end

#cleanup_handlerObject

Kills off the connection threads if there are any hanging around.



175
176
177
178
179
180
181
182
183
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 175

def cleanup_handler
  # Kill any remaining handle_connection threads that might
  # be hanging around
  stop_handler

  conn_threads.each { |thr|
    thr.kill
  }
end

#human_nameString

A string suitable for displaying to the user

Returns:

  • (String)


139
140
141
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 139

def human_name
  'bind AWS SSM'
end

#initialize(info = {}) ⇒ Object

Initializes a bind handler and adds the options common to all bind payloads, such as local port.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 147

def initialize(info = {})
  super

  register_options(
    [
      OptString.new('EC2_ID', [true, 'The EC2 ID of the instance ', '']),
      OptString.new('REGION', [true, 'AWS region containing the instance', 'us-east-1']),
      OptString.new('ACCESS_KEY_ID', [false, 'AWS access key', nil]),
      OptString.new('SECRET_ACCESS_KEY', [false, 'AWS secret key', nil]),
      OptString.new('ROLE_ARN', [false, 'AWS assumed role ARN', nil]),
      OptString.new('ROLE_SID', [false, 'AWS assumed role session ID', nil]),
    ], Msf::Handler::BindAwsSsm)

  register_advanced_options(
    [
      OptString.new('SSM_SESSION_DOC', [true, 'The SSM document to use for session requests', 'SSM-SessionManagerRunShell']),
      # AWS-RunShellScript, AWS-RunPowerShellScript, etc
      OptBool.new('SSM_KEEP_ALIVE', [false, 'Keep AWS SSM session alive with empty messages', true])
    ], Msf::Handler::BindAwsSsm)

  self.listener_threads = []
  self.conn_threads = []
  self.listener_pairs = {}
end

#payload_uriObject

A URI describing what the payload is configured to use for transport



295
296
297
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 295

def payload_uri
  "ssm://#{datastore['EC2_ID']}:0"
end

#start_handlerObject

Starts monitoring for an outbound connection to become established.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 202

def start_handler

  # Maximum number of seconds to run the handler
  ctimeout = 150

  # Maximum number of seconds to await initial API response
  rtimeout = 5

  if (exploit_config and exploit_config['active_timeout'])
    ctimeout = exploit_config['active_timeout'].to_i
  end

  # Ignore this if one of the required options is missing
  return if datastore['EC2_ID'].blank?

  # Only try the same host/port combination once
  return if self.listener_pairs[datastore['EC2_ID']]
  self.listener_pairs[datastore['EC2_ID']] = true

  # Start a new handling thread
  self.listener_threads << framework.threads.spawn("BindAwsSsmHandler-#{datastore['EC2_ID']}", false) do
    ssm_client = nil

    print_status("Started #{human_name} handler against #{datastore['EC2_ID']}:#{datastore['REGION']}")

    stime = Time.now.to_i

    while (stime + ctimeout > Time.now.to_i)
      begin
        ssm_client, peer_info = get_ssm_session
      rescue Rex::ConnectionError => e
        vprint_error(e.message)
      rescue
        wlog("Exception caught in AWS SSM handler: #{$!.class} #{$!}")
        break
      end
      break if ssm_client

      # Wait a half-second before trying again
      Rex::ThreadSafe.sleep(0.5)
    end

    # Valid client connection?
    if ssm_client
      # Increment the has connection counter
      self.pending_connections += 1

      # Timeout and datastore options need to be passed through to the client
      opts = {
        datastore: datastore,
        expiration: datastore['SessionExpirationTimeout'].to_i,
        comm_timeout: datastore['SessionCommunicationTimeout'].to_i,
        retry_total: datastore['SessionRetryTotal'].to_i,
        retry_wait: datastore['SessionRetryWait'].to_i
      }

      self.conn_threads << framework.threads.spawn('BindAwsSsmHandlerSession', false, ssm_client, peer_info) do |client_copy, info_copy|
        begin
          session_params = {
            target: datastore['EC2_ID'],
            document_name: datastore['SSM_SESSION_DOC']
          }

          # Call API to start SSM session
          session_init = client_copy.start_session(session_params)
          # Create WebSocket from parameters
          ssm_sock = connect_ssm_ws(session_init)
          # Create Channel from WebSocket
          chan = ssm_sock.to_ssm_channel
          # Configure Channel
          chan._start_ssm_keepalive if datastore['SSM_KEEP_ALIVE']
          chan.params.comm = Rex::Socket::Comm::Local unless chan.params.comm
          chan.params.peerhost = peer_info['IpAddress']
          chan.params.peerport = 0
          chan.params.peerhostname = peer_info['ComputerName']
          chan.update_term_size
        rescue => e
          print_error("AWS SSM handler failed: #{e.message}")
          elog('Exception raised from BindAwsSsm', error: e)
          return
        end

        self.listener_pairs[datastore['EC2_ID']] = chan

        handle_connection(chan.lsock, { datastore: datastore, aws_ssm_host_info: peer_info })
      end
    else
      wlog('No connection received before the handler completed')
    end
  end
end

#stop_handlerObject



299
300
301
302
303
304
305
306
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 299

def stop_handler
  # Stop the listener threads
  self.listener_threads.each do |t|
    t.kill
  end
  self.listener_threads = []
  self.listener_pairs = {}
end