metasploit-framework/lib/net/ssh/command_stream.rb

107 lines
2.1 KiB
Ruby

require 'rex'
module Net
module SSH
class CommandStream
attr_accessor :channel, :thread, :error, :ssh
attr_accessor :lsock, :rsock, :monitor
module PeerInfo
include ::Rex::IO::Stream
attr_accessor :peerinfo
attr_accessor :localinfo
end
def initialize(ssh, cmd, cleanup = false)
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
self.thread = Thread.new(ssh,cmd,cleanup) do |rssh,rcmd,rcleanup|
begin
info = rssh.transport.socket.getpeername
self.lsock.peerinfo = "#{info[1]}:#{info[2]}"
info = rssh.transport.socket.getsockname
self.lsock.localinfo = "#{info[1]}:#{info[2]}"
rssh.open_channel do |rch|
rch.exec(rcmd) do |c, success|
raise "could not execute command: #{rcmd.inspect}" unless success
c[:data] = ''
c.on_eof do
self.rsock.close rescue nil
self.ssh.close rescue nil
self.thread.kill
end
c.on_close do
self.rsock.close rescue nil
self.ssh.close rescue nil
self.thread.kill
end
c.on_data do |ch,data|
self.rsock.write(data)
end
c.on_extended_data do |ch, ctype, data|
self.rsock.write(data)
end
self.channel = c
end
end
self.monitor = Thread.new do
while(true)
next if not self.rsock.has_read_data?(1.0)
buff = self.rsock.sysread(16384)
break if not buff
verify_channel
self.channel.send_data(buff) if buff
end
end
while true
rssh.process(0.5) { true }
end
rescue ::Exception => e
self.error = e
#::Kernel.warn "BOO: #{e.inspect}"
#::Kernel.warn e.backtrace.join("\n")
ensure
self.monitor.kill if self.monitor
end
# Shut down the SSH session if requested
if(rcleanup)
rssh.close
end
end
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
end
end
end