# `pwnlib.tubes.ssh` — SSH

class pwnlib.tubes.ssh.ssh(user=None, host=None, port=22, password=None, key=None, keyfile=None, proxy_command=None, proxy_sock=None, level=None, cache=True, ssh_agent=False, ignore_config=False, raw=False, *a, **kw)

  Creates a new ssh connection.

  Parameters:

    - user (str) – The username to log in with

    - host (str) – The hostname to connect to

    - port (int) – The port to connect to

    - password (str) – Try to authenticate using this password

    - key (str) – Try to authenticate using this private key. The string should be the actual private key.

    - keyfile (str) – Try to authenticate using this private key. The string should be a filename.

    - proxy_command (str) – Use this as a proxy command. It has approximately the same semantics as ProxyCommand from ssh(1).

    - proxy_sock (str) – Use this socket instead of connecting to the host.

    - timeout – Timeout, in seconds

    - level – Log level

    - cache – Cache downloaded files (by hash/size/timestamp)

    - ssh_agent – If `True`, enable usage of keys via ssh-agent

    - ignore_config – If `True`, disable usage of ~/.ssh/config and ~/.ssh/authorized_keys

    - raw – If `True`, assume a non-standard shell and don’t probe the environment

  NOTE: The proxy_command and proxy_sock arguments is only available if a fairly new version of paramiko is used.

  Example proxying:

       >>> s1 = ssh(host='example.pwnme')
       >>> r1 = s1.remote('localhost', 22)
       >>> s2 = ssh(host='example.pwnme', proxy_sock=r1.sock)
       >>> r2 = s2.remote('localhost', 22) # and so on...
       >>> for x in r2, s2, r1, s1: x.close()

  __call__(attr)

    Permits function-style access to run commands over SSH

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> print(repr(s('echo hello')))
        b'hello'

  __getattr__(attr)

    Permits member access to run commands over SSH

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> s.echo('hello')
        b'hello'
        >>> s.whoami()
        b'travis'
        >>> s.echo(['huh','yay','args'])
        b'huh yay args'

  __getitem__(attr)

    Permits indexed access to run commands over SSH

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> print(repr(s['echo hello']))
        b'hello'

  __init__(user=None, host=None, port=22, password=None, key=None, keyfile=None, proxy_command=None, proxy_sock=None, level=None, cache=True, ssh_agent=False, ignore_config=False, raw=False, *a, **kw)

    Creates a new ssh connection.

    Parameters:

      - user (str) – The username to log in with

      - host (str) – The hostname to connect to

      - port (int) – The port to connect to

      - password (str) – Try to authenticate using this password

      - key (str) – Try to authenticate using this private key. The string should be the actual private key.

      - keyfile (str) – Try to authenticate using this private key. The string should be a filename.

      - proxy_command (str) – Use this as a proxy command. It has approximately the same semantics as ProxyCommand from ssh(1).

      - proxy_sock (str) – Use this socket instead of connecting to the host.

      - timeout – Timeout, in seconds

      - level – Log level

      - cache – Cache downloaded files (by hash/size/timestamp)

      - ssh_agent – If `True`, enable usage of keys via ssh-agent

      - ignore_config – If `True`, disable usage of ~/.ssh/config and ~/.ssh/authorized_keys

      - raw – If `True`, assume a non-standard shell and don’t probe the environment

    NOTE: The proxy_command and proxy_sock arguments is only available if a fairly new version of paramiko is used.

    Example proxying:

         >>> s1 = ssh(host='example.pwnme')
         >>> r1 = s1.remote('localhost', 22)
         >>> s2 = ssh(host='example.pwnme', proxy_sock=r1.sock)
         >>> r2 = s2.remote('localhost', 22) # and so on...
         >>> for x in r2, s2, r1, s1: x.close()

  __repr__()

    Return repr(self).

  _init_remote_platform_info()

    Fills _platform_info, e.g.:

        {'distro': 'Ubuntu\n',
         'distro_ver': '14.04\n',
         'machine': 'x86_64',
         'node': 'pwnable.kr',
         'processor': 'x86_64',
         'release': '3.11.0-12-generic',
         'system': 'linux',
         'version': '#19-ubuntu smp wed oct 9 16:20:46 utc 2013'}

  _libs_remote(remote)

    Return a dictionary of the libraries used by a remote file.

  checksec()

    Prints a helpful message about the remote system.

    Parameters:

      banner (bool) – Whether to print the path to the ELF binary.

  close()

    Close the connection.

  connect_remote(host, port, timeout=Timeout.default) → ssh_connecter

    Connects to a host through an SSH connection. This is equivalent to using the `-L` flag on `ssh`.

    Returns a `pwnlib.tubes.ssh.ssh_connecter` object.

    Examples

        >>> from pwn import *
        >>> l = listen()
        >>> s =  ssh(host='example.pwnme')
        >>> a = s.connect_remote(s.host, l.lport)
        >>> a=a; b = l.wait_for_connection()  # a=a; prevents hangs
        >>> a.sendline(b'Hello')
        >>> print(repr(b.recvline()))
        b'Hello\n'

  connected()

    Returns True if we are connected.

    Example

        >>> s =  ssh(host='example.pwnme')
        >>> s.connected()
        True
        >>> s.close()
        >>> s.connected()
        False

  download(file_or_directory, local=None)

    Download a file or directory from the remote host.

    Parameters:

      - file_or_directory (str) – Path to the file or directory to download.

      - local (str) – Local path to store the data. By default, uses the current directory.

    Examples

        >>> with open('/tmp/foobar','w+') as f:
        ...     _ = f.write('Hello, world')
        >>> s =  ssh(host='example.pwnme',
        ...         cache=False)
        >>> _ = s.set_working_directory('/tmp')
        >>> _ = s.download('foobar', 'barfoo')
        >>> with open('barfoo','r') as f:
        ...     print(f.read())
        Hello, world

  download_data(remote)

    Downloads a file from the remote server and returns it as a string.

    Parameters:

      remote (str) – The remote filename to download.

    Examples

        >>> with open('/tmp/bar','w+') as f:
        ...     _ = f.write('Hello, world')
        >>> s =  ssh(host='example.pwnme',
        ...         cache=False)
        >>> s.download_data('/tmp/bar')
        b'Hello, world'
        >>> s._sftp = None
        >>> s._tried_sftp = True
        >>> s.download_data('/tmp/bar')
        b'Hello, world'

  download_dir(remote=None, local=None, ignore_failed_read=False)

    Recursively downloads a directory from the remote server

    Parameters:

      - local – Local directory

      - remote – Remote directory

  download_file(remote, local=None)

    Downloads a file from the remote server.

    The file is cached in /tmp/pwntools-ssh-cache using a hash of the file, so calling the function twice has little overhead.

    Parameters:

      - remote (str/bytes) – The remote filename to download

      - local (str) – The local filename to save it to. Default is to infer it from the remote filename.

    Examples

        >>> with open('/tmp/foobar','w+') as f:
        ...     _ = f.write('Hello, world')
        >>> s =  ssh(host='example.pwnme',
        ...         cache=False)
        >>> _ = s.set_working_directory(wd='/tmp')
        >>> _ = s.download_file('foobar', 'barfoo')
        >>> with open('barfoo','r') as f:
        ...     print(f.read())
        Hello, world

  get(file_or_directory, local=None)

    download(file_or_directory, local=None)

    Download a file or directory from the remote host.

    Parameters:

      - file_or_directory (str) – Path to the file or directory to download.

      - local (str) – Local path to store the data. By default, uses the current directory.

    Examples

        >>> with open('/tmp/foobar','w+') as f:
        ...     _ = f.write('Hello, world')
        >>> s =  ssh(host='example.pwnme',
        ...         cache=False)
        >>> _ = s.set_working_directory('/tmp')
        >>> _ = s.download('foobar', 'barfoo')
        >>> with open('barfoo','r') as f:
        ...     print(f.read())
        Hello, world

  getenv(variable, **kwargs)

    Retrieve the address of an environment variable on the remote system.

    Note

    The exact address will differ based on what other environment variables are set, as well as argv[0]. In order to ensure that the path is exactly the same, it is recommended to invoke the process with `argv=[]`.

  interactive(shell=None)

    Create an interactive session.

    This is a simple wrapper for creating a new `pwnlib.tubes.ssh.ssh_channel` object and calling `pwnlib.tubes.ssh.ssh_channel.interactive()` on it.

  libs(remote, directory=None)

    Downloads the libraries referred to by a file.

    This is done by running ldd on the remote server, parsing the output and downloading the relevant files.

    The directory argument specified where to download the files. This defaults to ‘./$HOSTNAME’ where $HOSTNAME is the hostname of the remote server.

  listen(port=0, bind_address='', timeout=pwnlib.timeout.Timeout.default)

    listen_remote(port = 0, bind_address = ‘’, timeout = Timeout.default) -> ssh_connecter

    Listens remotely through an SSH connection. This is equivalent to using the `-R` flag on `ssh`.

    Returns a `pwnlib.tubes.ssh.ssh_listener` object.

    Examples

        >>> from pwn import *
        >>> s =  ssh(host='example.pwnme')
        >>> l = s.listen_remote()
        >>> a = remote(s.host, l.port)
        >>> a=a; b = l.wait_for_connection()  # a=a; prevents hangs
        >>> a.sendline(b'Hello')
        >>> print(repr(b.recvline()))
        b'Hello\n'

  listen_remote(port=0, bind_address='', timeout=Timeout.default) → ssh_connecter

    Listens remotely through an SSH connection. This is equivalent to using the `-R` flag on `ssh`.

    Returns a `pwnlib.tubes.ssh.ssh_listener` object.

    Examples

        >>> from pwn import *
        >>> s =  ssh(host='example.pwnme')
        >>> l = s.listen_remote()
        >>> a = remote(s.host, l.port)
        >>> a=a; b = l.wait_for_connection()  # a=a; prevents hangs
        >>> a.sendline(b'Hello')
        >>> print(repr(b.recvline()))
        b'Hello\n'

  process(argv=None, executable=None, tty=True, cwd=None, env=None, timeout=pwnlib.timeout.Timeout.default, run=True, stdin=0, stdout=1, stderr=2, preexec_fn=None, preexec_args=(), raw=True, aslr=None, setuid=None, shell=False)

    Executes a process on the remote server, in the same fashion as pwnlib.tubes.process.process.

    To achieve this, a Python script is created to call `os.execve` with the appropriate arguments.

    As an added bonus, the `ssh_channel` object returned has a `pid` property for the process pid.

    Parameters:

      - argv (list) – List of arguments to pass into the process

      - executable (str) – Path to the executable to run. If `None`, `argv[0]` is used.

      - tty (bool) – Request a tty from the server. This usually fixes buffering problems by causing libc to write data immediately rather than buffering it. However, this disables interpretation of control codes (e.g. Ctrl+C) and breaks .shutdown.

      - cwd (str) – Working directory. If `None`, uses the working directory specified on `cwd` or set via `set_working_directory()`.

      - env (dict) – Environment variables to set in the child. If `None`, inherits the default environment.

      - timeout (int) – Timeout to set on the tube created to interact with the process.

      - run (bool) – Set to `True` to run the program (default). If `False`, returns the path to an executable Python script on the remote server which, when executed, will do it.

      - stdin (int, str) – If an integer, replace stdin with the numbered file descriptor. If a string, a open a file with the specified path and replace stdin with its file descriptor. May also be one of `sys.stdin`, `sys.stdout`, `sys.stderr`. If `None`, the file descriptor is closed.

      - stdout (int, str) – See `stdin`.

      - stderr (int, str) – See `stdin`.

      - preexec_fn (callable) – Function which is executed on the remote side before execve(). This MUST be a self-contained function – it must perform all of its own imports, and cannot refer to variables outside its scope.

      - preexec_args (object) – Argument passed to `preexec_fn`. This MUST only consist of native Python objects.

      - raw (bool) – If `True`, disable TTY control code interpretation.

      - aslr (bool) – See `pwnlib.tubes.process.process` for more information.

      - setuid (bool) – See `pwnlib.tubes.process.process` for more information.

      - shell (bool) – Pass the command-line arguments to the shell.

    Returns:

      A new SSH channel, or a path to a script if `run=False`.

    Notes

    Requires Python on the remote server.

    Examples

        >>> s = ssh(host='example.pwnme')
        >>> sh = s.process('/bin/sh', env={'PS1':''})
        >>> sh.sendline(b'echo Hello; exit')
        >>> sh.recvall()
        b'Hello\n'
        >>> s.process(['/bin/echo', b'\xff']).recvall()
        b'\xff\n'
        >>> s.process(['readlink', '/proc/self/exe']).recvall()
        b'.../bin/readlink\n'
        >>> s.process(['LOLOLOL', '/proc/self/exe'], executable='readlink').recvall()
        b'.../bin/readlink\n'
        >>> s.process(['LOLOLOL\x00', '/proc/self/cmdline'], executable='cat').recvall()
        b'LOLOLOL\x00/proc/self/cmdline\x00'
        >>> sh = s.process(executable='/bin/sh')
        >>> str(sh.pid).encode() in s.pidof('sh')
        True
        >>> io = s.process(['pwd'], cwd='/tmp')
        >>> io.recvall()
        b'/tmp\n'
        >>> io.cwd
        '/tmp'
        >>> p = s.process(['python3','-c','import os; os.write(1, os.read(2, 1024))'], stderr=0)
        >>> p.send(b'hello')
        >>> p.recv()
        b'hello'
        >>> s.process(['/bin/echo', 'hello']).recvall()
        b'hello\n'
        >>> s.process(['/bin/echo', 'hello'], stdout='/dev/null').recvall()
        b''
        >>> s.process(['/usr/bin/env'], env={}).recvall()
        b''
        >>> s.process('/usr/bin/env', env={'A':'B'}).recvall()
        b'A=B\n'

        >>> s.process('false', preexec_fn=1234)
        Traceback (most recent call last):
        ...
        PwnlibException: preexec_fn must be a function

        >>> s.process('false', preexec_fn=lambda: 1234)
        Traceback (most recent call last):
        ...
        PwnlibException: preexec_fn cannot be a lambda

        >>> def uses_globals():
        ...     foo = bar
        >>> print(s.process('false', preexec_fn=uses_globals).recvall().strip().decode())
        Traceback (most recent call last):
        ...
        NameError: ...name 'bar' is not defined

        >>> s.process('echo hello', shell=True).recvall()
        b'hello\n'

        >>> io = s.process(['cat'], timeout=5)
        >>> io.recvline()
        b''

        >>> # Testing that empty argv works
        >>> io = s.process([], executable='sh')
        >>> io.sendline(b'echo $0')
        >>> io.recvline()
        b'$ \n'
        >>> # Make sure that we have a shell
        >>> io.sendline(b'echo hello')
        >>> io.recvline()
        b'$ hello\n'

        >>> # Testing that empty argv[0] works
        >>> io = s.process([''], executable='sh')
        >>> io.sendline(b'echo $0')
        >>> io.recvline()
        b'$ \n'

  put(file_or_directory, remote=None)

    upload(file_or_directory, remote=None)

    Upload a file or directory to the remote host.

    Parameters:

      - file_or_directory (str) – Path to the file or directory to download.

      - remote (str) – Local path to store the data. By default, uses the working directory.

  read(path)

    Wrapper around download_data to match `pwnlib.util.misc.read()`

  remote(host, port, timeout=pwnlib.timeout.Timeout.default)

    connect_remote(host, port, timeout = Timeout.default) -> ssh_connecter

    Connects to a host through an SSH connection. This is equivalent to using the `-L` flag on `ssh`.

    Returns a `pwnlib.tubes.ssh.ssh_connecter` object.

    Examples

        >>> from pwn import *
        >>> l = listen()
        >>> s =  ssh(host='example.pwnme')
        >>> a = s.connect_remote(s.host, l.lport)
        >>> a=a; b = l.wait_for_connection()  # a=a; prevents hangs
        >>> a.sendline(b'Hello')
        >>> print(repr(b.recvline()))
        b'Hello\n'

  run(process, tty=True, cwd=None, env=None, timeout=None, raw=True, wd=None)

    Backward compatibility. Use `system()`

  run_to_end(process, tty=False, cwd=None, env=None, timeout=Timeout.default) → str

    Run a command on the remote server and return a tuple with (data, exit_status). If tty is True, then the command is run inside a TTY on the remote server.

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> print(s.run_to_end('echo Hello; exit 17'))
        (b'Hello\n', 17)

  set_working_directory(wd=None, symlink=False)

    Sets the working directory in which future commands will be run (via ssh.run) and to which files will be uploaded/downloaded from if no path is provided

    Note

    This uses `mktemp -d` under the covers, sets permissions on the directory to `0700`. This means that setuid binaries will not be able to access files created in this directory.

    In order to work around this, we also `chmod +x` the directory.

    Parameters:

      - wd (string) – Working directory. Default is to auto-generate a directory based on the result of running ‘mktemp -d’ on the remote machine.

      - symlink (bool,str) –

        Create symlinks in the new directory.

        The default value, `False`, implies that no symlinks should be created.

        A string value is treated as a path that should be symlinked. It is passed directly to the shell on the remote end for expansion, so wildcards work.

        Any other value is treated as a boolean, where `True` indicates that all files in the “old” working directory should be symlinked.

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> cwd = s.set_working_directory()
        >>> s.ls()
        b''
        >>> packing._decode(s.pwd()) == cwd
        True

        >>> s =  ssh(host='example.pwnme')
        >>> homedir = s.pwd()
        >>> _=s.touch('foo')

        >>> _=s.set_working_directory()
        >>> assert s.ls() == b''

        >>> _=s.set_working_directory(homedir)
        >>> assert b'foo' in s.ls().split(), s.ls().split()

        >>> _=s.set_working_directory(symlink=True)
        >>> assert b'foo' in s.ls().split(), s.ls().split()
        >>> assert homedir != s.pwd()

        >>> symlink=os.path.join(homedir,b'*')
        >>> _=s.set_working_directory(symlink=symlink)
        >>> assert b'foo' in s.ls().split(), s.ls().split()
        >>> assert homedir != s.pwd()

        >>> _=s.set_working_directory()
        >>> io = s.system('pwd')
        >>> io.recvallS().strip() == io.cwd
        True
        >>> io.cwd == s.cwd
        True

  shell(shell=None, tty=True, timeout=Timeout.default) → ssh_channel

    Open a new channel with a shell inside.

    Parameters:

      - shell (str) – Path to the shell program to run. If `None`, uses the default shell for the logged in user.

      - tty (bool) – If `True`, then a TTY is requested on the remote server.

    Returns:

      Return a `pwnlib.tubes.ssh.ssh_channel` object.

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> sh = s.shell('/bin/sh')
        >>> sh.sendline(b'echo Hello; exit')
        >>> print(b'Hello' in sh.recvall())
        True

  system(process, tty=True, cwd=None, env=None, timeout=Timeout.default, raw=True) → ssh_channel

    Open a new channel with a specific process inside. If tty is True, then a TTY is requested on the remote server.

    If raw is True, terminal control codes are ignored and input is not echoed back.

    Return a `pwnlib.tubes.ssh.ssh_channel` object.

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> py = s.system('python3 -i')
        >>> _ = py.recvuntil(b'>>> ')
        >>> py.sendline(b'print(2+2)')
        >>> py.sendline(b'exit()')
        >>> print(repr(py.recvline()))
        b'4\n'
        >>> s.system('env | grep -a AAAA', env={'AAAA': b'\x90'}).recvall()
        b'AAAA=\x90\n'
        >>> io = s.system('pwd', cwd='/tmp')
        >>> io.recvall()
        b'/tmp\n'
        >>> io.cwd
        '/tmp'

  unlink(file)

    Delete the file on the remote host

    Parameters:

      file (str) – Path to the file

  upload(file_or_directory, remote=None)

    Upload a file or directory to the remote host.

    Parameters:

      - file_or_directory (str) – Path to the file or directory to download.

      - remote (str) – Local path to store the data. By default, uses the working directory.

  upload_data(data, remote)

    Uploads some data into a file on the remote server.

    Parameters:

      - data (str) – The data to upload.

      - remote (str) – The filename to upload it to.

    Example

        >>> s =  ssh(host='example.pwnme')
        >>> s.upload_data(b'Hello, world', '/tmp/upload_foo')
        >>> print(open('/tmp/upload_foo').read())
        Hello, world
        >>> s._sftp = False
        >>> s._tried_sftp = True
        >>> s.upload_data(b'Hello, world', '/tmp/upload_bar')
        >>> print(open('/tmp/upload_bar').read())
        Hello, world

  upload_dir(local, remote=None)

    Recursively uploads a directory onto the remote server

    Parameters:

      - local – Local directory

      - remote – Remote directory

  upload_file(filename, remote=None)

    Uploads a file to the remote server. Returns the remote filename.

    Arguments: filename(str): The local filename to download remote(str): The remote filename to save it to. Default is to infer it from the local filename.

  which(program) → str

    Minor modification to just directly invoking `which` on the remote system which adds the current working directory to the end of `$PATH`.

  write(path, data)

    Wrapper around upload_data to match `pwnlib.util.misc.write()`

  property arch

    CPU Architecture of the remote machine.

    Type:

      `str`

  property aslr

    Whether ASLR is enabled on the system.

    Example

        >>> s = ssh("travis", "example.pwnme")
        >>> s.aslr
        True

    Type:

      `bool`

  property aslr_ulimit

    Whether the entropy of 32-bit processes can be reduced with ulimit.

    Type:

      `bool`

  property bits

    Pointer size of the remote machine.

    Type:

      `str`

  cache = True

    Enable caching of SSH downloads (`bool`)

  client = None

    Paramiko SSHClient which backs this object

  property distro

    Linux distribution name and release.

    Type:

      `tuple`

  host = None

    Remote host name (`str`)

  property os

    Operating System of the remote machine.

    Type:

      `str`

  pid = None

    PID of the remote `sshd` process servicing this connection.

  port = None

    Remote port (`int`)

  property sftp

    Paramiko SFTPClient object which is used for file transfers. Set to `None` to disable `sftp`.

  property version

    Kernel version of the remote machine.

    Type:

      `tuple`

class pwnlib.tubes.ssh.ssh_channel

  Bases: `sock`

  interactive(prompt=pwnlib.term.text.bold_red('$') + ' ')

    If not in TTY-mode, this does exactly the same as meth:pwnlib.tubes.tube.tube.interactive, otherwise it does mostly the same.

    An SSH connection in TTY-mode will typically supply its own prompt, thus the prompt argument is ignored in this case. We also have a few SSH-specific hacks that will ideally be removed once the `pwnlib.term` is more mature.

  kill()

    Kills the process.

  poll() → int

    Poll the exit code of the process. Will return None, if the process has not yet finished and the exit code otherwise.

class pwnlib.tubes.ssh.ssh_process(parent, process=None, tty=False, cwd=None, env=None, raw=True, *args, **kwargs)

  Bases: `ssh_channel`

  getenv(variable, **kwargs)

    Retrieve the address of an environment variable in the remote process.

    Examples

        >>> s = ssh(host='example.pwnme')
        >>> p = s.process(['python3', '-c', 'import time; time.sleep(10)'])
        >>> hex(p.getenv('PATH'))
        '0x...'

  libs() → dict

    Returns a dictionary mapping the address of each loaded library in the process’s address space.

    If `/proc/$PID/maps` cannot be opened, the output of ldd is used verbatim, which may be different than the actual addresses if ASLR is enabled.

  argv = None

    Arguments passed to the process Only valid when instantiated through `ssh.process()`

  cwd = None

    Working directory

  property elf

    Returns an ELF file for the executable that launched the process.

  executable = None

    Executable of the procesks Only valid when instantiated through `ssh.process()`

  property libc

    Returns an ELF for the libc for the current process. If possible, it is adjusted to the correct address automatically.

    Examples

        >>> s =  ssh(host='example.pwnme')
        >>> p = s.process('true')
        >>> p.libc
        ELF(.../libc.so.6')

  pid = None

    PID of the process Only valid when instantiated through `ssh.process()`

class pwnlib.tubes.ssh.ssh_connecter

  Bases: `sock`

class pwnlib.tubes.ssh.ssh_listener

  Bases: `sock`