Class: Msf::Handler::BindAwsSsm::AwsSsmSessionChannel
- Inherits:
-
Object
- Object
- Msf::Handler::BindAwsSsm::AwsSsmSessionChannel
- Includes:
- Rex::IO::StreamAbstraction
- Defined in:
- lib/msf/core/handler/bind_aws_ssm.rb
Overview
This module implements SSM R/W abstraction to mimic Rex::IO::Stream interfaces These methods are not fully synchronized/thread-safe as the req/resp chain is itself async and rely on a cursor to obtain responses when they are ready from the SSM API.
Instance Method Summary collapse
-
#close ⇒ Object
Closes the stream abstraction and kills the monitor thread.
-
#initialize(framework, ssmclient, peer_info) ⇒ AwsSsmSessionChannel
constructor
A new instance of AwsSsmSessionChannel.
-
#monitor_shell_stdout ⇒ Object
Funnel data from the shell’s stdout to
rsock
. -
#ssm_read(length = nil, opts = {}) ⇒ Object
Find command response on cursor and return to caller - doesn’t respect length arg, yet.
- #write(buf, opts = {}) ⇒ Object
Constructor Details
#initialize(framework, ssmclient, peer_info) ⇒ AwsSsmSessionChannel
Returns a new instance of AwsSsmSessionChannel.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 30 def initialize(framework, ssmclient, peer_info) @framework = framework @peer_info = peer_info @ssmclient = ssmclient @cursor = nil @cmd_doc = peer_info['CommandDocument'] initialize_abstraction self.lsock.extend(AwsSsmSessionChannelExt) # self.lsock.peerinfo = peer_info['ComputerName'] + ':0' self.lsock.peerinfo = peer_info['IpAddress'] + ':0' # Fudge the portspec since each client request is actually a new connection w/ a new source port, for now self.lsock.localinfo = Rex::Socket.source_address(@ssmclient.config.endpoint.to_s.sub('https://', '')) + ':0' monitor_shell_stdout end |
Instance Method Details
#close ⇒ Object
Closes the stream abstraction and kills the monitor thread.
114 115 116 117 118 119 |
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 114 def close @monitor_thread.kill if (@monitor_thread) @monitor_thread = nil cleanup_abstraction end |
#monitor_shell_stdout ⇒ Object
Funnel data from the shell’s stdout to rsock
StreamAbstraction#monitor_rsock will deal with getting data from the client (user input). From there, it calls our write() below, funneling the data to the shell’s stdin on the other side.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 55 def monitor_shell_stdout @monitor_thread = @framework.threads.spawn('AwsSsmSessionHandlerMonitor', false) { begin while true Rex::ThreadSafe.sleep(0.5) while @cursor.nil? # Handle data from the API and write to the client buf = ssm_read break if buf.nil? rsock.put(buf) end rescue ::Exception => e ilog("AwsSsmSession monitor thread raised #{e.class}: #{e}") end } end |
#ssm_read(length = nil, opts = {}) ⇒ Object
Find command response on cursor and return to caller - doesn’t respect length arg, yet
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 72 def ssm_read(length = nil, opts = {}) maxw = opts[:timeout] ? opts[:timeout] : 30 start = Time.now resp = @ssmclient.list_command_invocations(command_id: @cursor, instance_id: @peer_info['InstanceId'], details: true) while (resp.command_invocations.empty? or resp.command_invocations[0].status == 'InProgress') and (Time.now - start).to_i.abs < maxw do Rex::ThreadSafe.sleep(1) resp = @ssmclient.list_command_invocations(command_id: @cursor, instance_id: @peer_info['InstanceId'], details: true) end # SSM script invocation states are: InProgress, Success, TimedOut, Cancelled, Failed if resp.command_invocations[0].status == 'Success' or resp.command_invocations[0].status == 'Failed' # The big limitation: SSM command outputs are only 2500 chars max, otherwise you have to write to S3 and read from there output = resp.command_invocations.map {|c| c.command_plugins.map {|p| p.output}.join}.join @cursor = nil return output else @cursor = nil ilog("AwsSsmSession error #{resp}") raise resp end nil end |
#write(buf, opts = {}) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/msf/core/handler/bind_aws_ssm.rb', line 95 def write(buf, opts = {}) resp = @ssmclient.send_command( document_name: @cmd_doc, instance_ids: [@peer_info['InstanceId']], parameters: { commands: [buf] } ) if resp.command.error_count == 0 @cursor = resp.command.command_id return buf.length else @cursor = nil ilog("AwsSsmSession error #{resp}") raise resp end end |