2016-06-22 20:02:36 +00:00
|
|
|
# -*- coding: binary -*-
|
|
|
|
|
2018-11-02 01:11:19 +00:00
|
|
|
class Net::SSH::CommandStream
|
2016-06-22 20:02:36 +00:00
|
|
|
|
|
|
|
attr_accessor :channel, :thread, :error, :ssh
|
|
|
|
attr_accessor :lsock, :rsock, :monitor
|
|
|
|
|
|
|
|
module PeerInfo
|
|
|
|
include ::Rex::IO::Stream
|
|
|
|
attr_accessor :peerinfo
|
|
|
|
attr_accessor :localinfo
|
|
|
|
end
|
|
|
|
|
2018-02-08 08:21:16 +00:00
|
|
|
def shell_requested(channel, success)
|
2018-10-19 03:38:10 +00:00
|
|
|
unless success
|
2018-11-02 01:11:19 +00:00
|
|
|
raise Net::SSH::ChannelRequestFailed, 'Shell/exec channel request failed'
|
2018-10-19 03:38:10 +00:00
|
|
|
end
|
|
|
|
|
2018-11-02 01:11:19 +00:00
|
|
|
self.channel = channel
|
|
|
|
|
2018-02-08 08:21:16 +00:00
|
|
|
channel[:data] = ''
|
2018-10-23 13:45:38 +00:00
|
|
|
channel[:extended_data] = ''
|
2018-02-08 08:21:16 +00:00
|
|
|
|
|
|
|
channel.on_eof do
|
2018-07-25 16:22:28 +00:00
|
|
|
cleanup
|
2018-02-08 08:21:16 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
channel.on_close do
|
2018-07-25 16:22:28 +00:00
|
|
|
cleanup
|
2018-02-08 08:21:16 +00:00
|
|
|
end
|
|
|
|
|
2018-10-19 03:38:10 +00:00
|
|
|
channel.on_data do |ch, data|
|
2018-02-08 08:21:16 +00:00
|
|
|
self.rsock.write(data)
|
2018-10-23 13:45:38 +00:00
|
|
|
channel[:data] << data
|
2018-02-08 08:21:16 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
channel.on_extended_data do |ch, ctype, data|
|
|
|
|
self.rsock.write(data)
|
2018-10-23 13:45:38 +00:00
|
|
|
channel[:extended_data] << data
|
2018-02-08 08:21:16 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-17 20:40:46 +00:00
|
|
|
def initialize(ssh, cmd = nil, pty: false, cleanup: false)
|
2016-06-22 20:02:36 +00:00
|
|
|
self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
|
|
|
|
self.lsock.extend(Rex::IO::Stream)
|
|
|
|
self.lsock.extend(PeerInfo)
|
|
|
|
self.rsock.extend(Rex::IO::Stream)
|
|
|
|
|
|
|
|
self.ssh = ssh
|
2018-10-17 20:45:02 +00:00
|
|
|
self.thread = Thread.new(ssh, cmd, pty, cleanup) do |rssh, rcmd, rpty, rcleanup|
|
2018-11-02 01:11:19 +00:00
|
|
|
info = rssh.transport.socket.getpeername_as_array
|
|
|
|
self.lsock.peerinfo = "#{info[1]}:#{info[2]}"
|
|
|
|
|
|
|
|
info = rssh.transport.socket.getsockname
|
|
|
|
self.lsock.localinfo = "#{info[1]}:#{info[2]}"
|
|
|
|
|
|
|
|
channel = rssh.open_channel do |rch|
|
|
|
|
# A PTY will write us to {u,w}tmp and lastlog
|
|
|
|
rch.request_pty if rpty
|
2016-06-22 20:02:36 +00:00
|
|
|
|
2018-11-02 01:11:19 +00:00
|
|
|
if rcmd.nil?
|
|
|
|
rch.send_channel_request('shell', &method(:shell_requested))
|
|
|
|
else
|
|
|
|
rch.exec(rcmd, &method(:shell_requested))
|
2016-06-22 20:02:36 +00:00
|
|
|
end
|
2018-11-02 01:11:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
channel.on_open_failed do |ch, code, desc|
|
|
|
|
raise Net::SSH::ChannelOpenFailed.new(code, 'Session channel open failed')
|
|
|
|
end
|
2016-06-22 20:02:36 +00:00
|
|
|
|
2018-11-02 01:11:19 +00:00
|
|
|
self.monitor = Thread.new do
|
|
|
|
while(true)
|
|
|
|
next if not self.rsock.has_read_data?(1.0)
|
|
|
|
buff = self.rsock.read(16384)
|
|
|
|
break if not buff
|
|
|
|
verify_channel
|
|
|
|
self.channel.send_data(buff) if buff
|
2016-06-22 20:02:36 +00:00
|
|
|
end
|
2018-11-02 01:11:19 +00:00
|
|
|
end
|
2016-06-22 20:02:36 +00:00
|
|
|
|
2018-11-02 01:11:19 +00:00
|
|
|
while true
|
|
|
|
rssh.process(0.5) { true }
|
2016-06-22 20:02:36 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Shut down the SSH session if requested
|
2018-07-25 16:22:28 +00:00
|
|
|
if !rcmd.nil? && rcleanup
|
2016-06-22 20:02:36 +00:00
|
|
|
rssh.close
|
|
|
|
end
|
|
|
|
end
|
2018-11-02 01:11:19 +00:00
|
|
|
rescue ::Exception => e
|
|
|
|
# XXX: This won't be set UNTIL there's a failure from a thread
|
|
|
|
self.error = e
|
|
|
|
ensure
|
|
|
|
self.monitor.kill if self.monitor
|
2016-06-22 20:02:36 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Prevent a race condition
|
|
|
|
#
|
|
|
|
def verify_channel
|
|
|
|
while ! self.channel
|
|
|
|
raise EOFError if ! self.thread.alive?
|
|
|
|
::IO.select(nil, nil, nil, 0.10)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-07-25 16:22:28 +00:00
|
|
|
def cleanup
|
|
|
|
self.monitor.kill
|
|
|
|
self.lsock.close rescue nil
|
|
|
|
self.rsock.close rescue nil
|
|
|
|
self.ssh.close rescue nil
|
|
|
|
self.thread.kill
|
|
|
|
end
|
|
|
|
|
2016-06-22 20:02:36 +00:00
|
|
|
end
|