Module: Msf::Handler::BindAwsInstanceConnect

Includes:
Msf::Handler
Defined in:
lib/msf/core/handler/bind_aws_instance_connect.rb

Overview

This module implements the AWS InstanceConnect handler. This means that it will attempt to connect to a remote host through the AWS InstanceConnect pipe for a period of time (typically the duration of an exploit) to see if the agent has started listening.

Defined Under Namespace

Modules: AwsInstanceConnectSessionChannelExt

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

Instance Attribute Details

#conn_threadsObject (protected)

:nodoc:



350
351
352
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 350

def conn_threads
  @conn_threads
end

#listener_pairsObject (protected)

:nodoc:



352
353
354
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 352

def listener_pairs
  @listener_pairs
end

#listener_threadsObject (protected)

:nodoc:



351
352
353
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 351

def listener_threads
  @listener_threads
end

Class Method Details

.general_handler_typeObject

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



31
32
33
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 31

def self.general_handler_type
  'bind'
end

.handler_typeObject

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



24
25
26
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 24

def self.handler_type
  'bind_aws_instance_connect'
end

Instance Method Details

#add_handler(opts = {}) ⇒ Object

Starts a new connecting thread



97
98
99
100
101
102
103
104
105
106
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 97

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.



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 81

def cleanup_handler
  # Kill any remaining handle_connection threads that might
  # be hanging around
  stop_handler
  conn_threads.each { |thr|
    begin
      thr.kill
    rescue => e
      elog(e)
    end
  }
end

#comm_stringObject



196
197
198
199
200
201
202
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 196

def comm_string
  if self.listener_pairs[datastore['EC2_ID']].nil?
    "(setting up)"
  else
    "(via #{ssh_url})"
  end
end

#human_nameString

A string suitable for displaying to the user

Returns:

  • (String)


38
39
40
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 38

def human_name
  'bind AWS InstanceConnect'
end

#initialize(info = {}) ⇒ Object

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



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 46

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('INSTANCE_USER', [false, 'Username on the EC2 instance with which to log-in']),
      OptString.new('ROLE_ARN', [false, 'AWS assumed role ARN', nil]),
      OptString.new('ROLE_SID', [false, 'AWS assumed role session ID', nil]),
      OptString.new('USERNAME', [false, 'EC2 instance local username to authenticate with']),
      OptString.new('PASSWORD', [false, 'EC2 instance local password to authenticate with'])
    ], Msf::Handler::BindAwsInstanceConnect)

  register_advanced_options(
    [
      OptString.new('PRIVATE_KEY', [
        false,
        'The string value of the private key that will be used. If you are using MSFConsole,
        this value should be set as file:PRIVATE_KEY_PATH. OpenSSH, RSA, DSA, and ECDSA private keys are supported.'
      ]),
      OptString.new('KEY_PASS', [false, 'Passphrase for SSH private key(s)']),
      OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false])
    ], Msf::Handler::BindAwsInstanceConnect)

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

#payload_uriObject

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



192
193
194
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 192

def payload_uri
  "serial+ssh://#{datastore['EC2_ID']}:#{INSTANCE_PORT}"
end

#start_handlerObject

Starts monitoring for an outbound connection to become established.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 111

def start_handler
  if datastore['EC2_ID'].blank?
    raise Msf::OptionValidateError.new({ 'EC2_ID' => "EC2_ID cannot be blank" })
  end

  # 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
  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("BindAwsInstanceConnectHandler-#{datastore['EC2_ID']}", false) {
    instance_connect_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
        # Call API to start InstanceConnect session
        if start_instance_connect_session
          instance_connect_client = connect_ssh
        else
          raise Rex::ConnectionError.new('Cannot establish serial connection to ' + datastore['EC2_ID'])
        end
      rescue Aws::EC2InstanceConnect::Errors::SerialConsoleSessionLimitExceededException => e
        vprint_error("Too many active serial console sessions. It takes 30 seconds to tear down a session after you've disconnected from the serial console in order to allow a new session.")
      rescue Aws::Errors::ServiceError => e
        vprint_error(e.message)
      rescue Rex::ConnectionError => e
        vprint_error(e.message)
      rescue StandardError => e
        vprint_error(e.message)
        elog("Exception caught in InstanceConnect handler: #{$!.class} #{$!}", error: e)
        break
      end
      break if instance_connect_client

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

    # Valid client connection?
    if (instance_connect_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,
        :serial_username => datastore['USERNAME'],
        :serial_password => datastore['PASSWORD']
      }

      self.conn_threads << framework.threads.spawn("BindAwsInstanceConnectHandlerSession", false, instance_connect_client, opts) { |ssh, opts_copy|
        begin
          self.listener_pairs[datastore['EC2_ID']] = ssh
          handle_connection(ssh, opts_copy)
        rescue => e
          elog('Exception raised from BindAwsInstanceConnect.handle_connection', error: e)
        end
      }
    else
      wlog("No connection received before the handler completed")
    end
  }
end

#stop_handlerObject



204
205
206
207
208
209
210
211
# File 'lib/msf/core/handler/bind_aws_instance_connect.rb', line 204

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