我正在尝试paramiko通过netcat弹回SSH会话:
paramiko
MyLocalMachine ----||----> MiddleMachine --(netcat)--> AnotherMachine ('localhost') (firewall) ('1.1.1.1') ('2.2.2.2')
MyLocalMachine
AnotherMachine
MiddleMachine
sshpass
PExpect
我可以使用以下代码部分实现此目的:
cli = paramiko.SSHClient() cli.set_missing_host_key_policy(paramiko.AutoAddPolicy()) proxy = paramiko.ProxyCommand('ssh user@1.1.1.1 nc 2.2.2.2 22') cli.connect(hostname='2.2.2.2', username='user', password='pass', sock=proxy)
问题是,因为ProxyCommand使用subprocess.Popen运行给定的命令,它要求我给密码“特设”,从用户输入(也,它需要在操作系统MyLocalMachine已ssh安装- 这是情况并非总是如此)。
ProxyCommand
subprocess.Popen
ssh
由于ProxyCommand的方法(recv,send)是对适当POpen方法的简单绑定,我想知道是否有可能欺骗paramiko客户端使用另一个客户端的会话作为代理?
recv
send
POpen
更新15.05.18:添加了丢失的代码(复制粘贴的上帝对我不利)。
TL; DR:我设法通过简单的exec_command调用和一个伪装成a的类来做到这一点sock。
exec_command
sock
总结一下:
nc
因此,这里是解决方案:
以下代码定义了一个可以代替的类paramiko.ProxyCommand。它提供了标准socket对象执行的所有方法。此类的init方法采用exec_command()通常返回的3-tupple :
paramiko.ProxyCommand
socket
exec_command()
注意:它已经由我进行了广泛的测试,但是您不应认为任何事情都是理所当然的。 这是一个hack。
import paramiko import time import socket from select import select class ParaProxy(paramiko.proxy.ProxyCommand): def __init__(self, stdin, stdout, stderr): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.timeout = None self.channel = stdin.channel def send(self, content): try: self.stdin.write(content) except IOError as exc: raise socket.error("Error: {}".format(exc)) return len(content) def recv(self, size): try: buffer = b'' start = time.time() while len(buffer) < size: select_timeout = self._calculate_remaining_time(start) ready, _, _ = select([self.stdout.channel], [], [], select_timeout) if ready and self.stdout.channel is ready[0]: buffer += self.stdout.read(size - len(buffer)) except socket.timeout: if not buffer: raise except IOError as e: return "" return buffer def _calculate_remaining_time(self, start): if self.timeout is not None: elapsed = time.time() - start if elapsed >= self.timeout: raise socket.timeout() return self.timeout - elapsed return None def close(self): self.stdin.close() self.stdout.close() self.stderr.close() self.channel.close()
下面显示了如何使用上述类解决问题:
# Connecting to MiddleMachine and executing netcat mid_cli = paramiko.SSHClient() mid_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy()) mid_cli.connect(hostname='1.1.1.1', username='user', password='pass') io_tupple = mid_cli.exec_command('nc 2.2.2.2 22') # Instantiate the 'masquerader' class proxy = ParaProxy(*io_tupple) # Connecting to AnotherMachine and executing... anything... end_cli = paramiko.SSHClient() end_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy()) end_cli.connect(hostname='2.2.2.2', username='user', password='pass', sock=proxy) end_cli.exec_command('echo THANK GOD FINALLY')
等等。