Skip to content

Commit c7f7cfd

Browse files
authored
Land rapid7#19656 Close ssh session on error
2 parents 31930f4 + 566e12b commit c7f7cfd

24 files changed

+60
-37
lines changed

lib/msf/base/sessions/aws_instance_connect_command_shell_bind.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def desc
7676
end
7777

7878
def bootstrap(datastore = {}, handler = nil)
79-
@ssh_command_stream = Net::SSH::CommandStream.new(ssh_connection)
79+
@ssh_command_stream = Net::SSH::CommandStream.new(ssh_connection, session: self, logger: self)
8080

8181
@ssh_command_stream.verify_channel
8282
# set remote_window_size to 32 which seems to help stability

lib/msf/base/sessions/ssh_command_shell_bind.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ def bootstrap(datastore = {}, handler = nil)
243243
# shells accessed through SSH may respond to the echo command issued for verification as expected
244244
datastore['AutoVerifySession'] &= @platform.blank?
245245

246-
@rstream = Net::SSH::CommandStream.new(ssh_connection).lsock
246+
@rstream = Net::SSH::CommandStream.new(ssh_connection, session: self, logger: self).lsock
247247
super
248248

249249
@info = "SSH #{username} @ #{@peer_info}"

lib/net/ssh/command_stream.rb

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
class Net::SSH::CommandStream
44

5-
attr_accessor :channel, :thread, :error, :ssh
5+
attr_accessor :channel, :thread, :error, :ssh, :session, :logger
66
attr_accessor :lsock, :rsock, :monitor
77

88
module PeerInfo
@@ -13,7 +13,8 @@ module PeerInfo
1313

1414
def shell_requested(channel, success)
1515
unless success
16-
raise Net::SSH::ChannelRequestFailed, 'Shell/exec channel request failed'
16+
error = Net::SSH::ChannelRequestFailed.new('Shell/exec channel request failed')
17+
handle_error(error: error)
1718
end
1819

1920
self.channel = channel
@@ -40,7 +41,9 @@ def shell_requested(channel, success)
4041
end
4142
end
4243

43-
def initialize(ssh, cmd = nil, pty: false, cleanup: false)
44+
def initialize(ssh, cmd = nil, pty: false, cleanup: false, session: nil, logger: nil)
45+
self.session = session
46+
self.logger = logger
4447
self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
4548
self.lsock.extend(Rex::IO::Stream)
4649
self.lsock.extend(PeerInfo)
@@ -74,31 +77,40 @@ def initialize(ssh, cmd = nil, pty: false, cleanup: false)
7477
end
7578

7679
channel.on_open_failed do |ch, code, desc|
77-
raise Net::SSH::ChannelOpenFailed.new(code, 'Session channel open failed')
80+
error = Net::SSH::ChannelOpenFailed.new(code, 'Session channel open failed')
81+
handle_error(error: error)
7882
end
7983

8084
self.monitor = Thread.new do
81-
while(true)
82-
next if not self.rsock.has_read_data?(1.0)
83-
buff = self.rsock.read(16384)
84-
break if not buff
85-
verify_channel
86-
self.channel.send_data(buff) if buff
85+
begin
86+
Kernel.loop do
87+
next if not self.rsock.has_read_data?(1.0)
88+
89+
buff = self.rsock.read(16384)
90+
break if not buff
91+
92+
verify_channel
93+
self.channel.send_data(buff) if buff
94+
end
95+
rescue ::StandardError => e
96+
handle_error(error: e)
8797
end
8898
end
8999

90-
while true
91-
rssh.process(0.5) { true }
100+
begin
101+
Kernel.loop { rssh.process(0.5) { true } }
102+
rescue ::StandardError => e
103+
handle_error(error: e)
92104
end
93105

94106
# Shut down the SSH session if requested
95107
if !rcmd.nil? && rcleanup
96108
rssh.close
97109
end
98110
end
99-
rescue ::Exception => e
111+
rescue ::StandardError => e
100112
# XXX: This won't be set UNTIL there's a failure from a thread
101-
self.error = e
113+
handle_error(error: e)
102114
ensure
103115
self.monitor.kill if self.monitor
104116
end
@@ -113,7 +125,18 @@ def verify_channel
113125
end
114126
end
115127

128+
def handle_error(error: nil)
129+
self.error = error if error
130+
131+
if self.logger
132+
self.logger.print_error("SSH Command Stream encountered an error: #{self.error} (Server Version: #{self.ssh.transport.server_version.version})")
133+
end
134+
135+
cleanup
136+
end
137+
116138
def cleanup
139+
self.session.alive = false if self.session
117140
self.monitor.kill
118141
self.lsock.close rescue nil
119142
self.rsock.close rescue nil

modules/auxiliary/scanner/ssh/eaton_xpert_backdoor.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def run_host(ip)
8484
info: version
8585
)
8686

87-
shell = Net::SSH::CommandStream.new(ssh)
87+
shell = Net::SSH::CommandStream.new(ssh, logger: self)
8888

8989
# XXX: Wait for CommandStream to log a channel request failure
9090
sleep 0.1

modules/auxiliary/scanner/ssh/fortinet_backdoor.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def run_host(ip)
7777
info: version
7878
)
7979

80-
shell = Net::SSH::CommandStream.new(ssh)
80+
shell = Net::SSH::CommandStream.new(ssh, logger: self)
8181

8282
# XXX: Wait for CommandStream to log a channel request failure
8383
sleep 0.1

modules/auxiliary/scanner/ssh/libssh_auth_bypass.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def run_host(ip)
120120
info: version
121121
)
122122

123-
shell = Net::SSH::CommandStream.new(ssh, datastore['CMD'], pty: datastore['SPAWN_PTY'])
123+
shell = Net::SSH::CommandStream.new(ssh, datastore['CMD'], pty: datastore['SPAWN_PTY'], logger: self)
124124

125125
# XXX: Wait for CommandStream to log a channel request failure
126126
sleep 0.1

modules/exploits/apple_ios/ssh/cydia_default_ssh.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def do_login(user, pass)
110110
end
111111

112112
if ssh
113-
conn = Net::SSH::CommandStream.new(ssh)
113+
conn = Net::SSH::CommandStream.new(ssh, logger: self)
114114
ssh = nil
115115
return conn
116116
end

modules/exploits/freebsd/http/junos_phprc_auto_prepend_file.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ def ssh_login
349349
end
350350

351351
if ssh
352-
Net::SSH::CommandStream.new(ssh)
352+
Net::SSH::CommandStream.new(ssh, logger: self)
353353
end
354354
end
355355

modules/exploits/linux/http/ubiquiti_airos_file_upload.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def ssh_login
156156
private: private_key,
157157
private_type: :ssh_key
158158
)
159-
return Net::SSH::CommandStream.new(ssh)
159+
return Net::SSH::CommandStream.new(ssh, logger: self)
160160
end
161161

162162
nil

modules/exploits/linux/ssh/ceragon_fibeair_known_privkey.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def do_login(user)
111111
if ssh_socket
112112

113113
# Create a new session from the socket, then dump it.
114-
conn = Net::SSH::CommandStream.new(ssh_socket)
114+
conn = Net::SSH::CommandStream.new(ssh_socket, logger: self)
115115
ssh_socket = nil
116116

117117
return conn

0 commit comments

Comments
 (0)