A pure-Python implementation of the SSHv2 protocol for scripting remote command execution, SFTP file transfer, and tunnelling.
Paramiko lets you open SSH sessions, run commands, and move files programmatically without shelling out to the ssh binary. It underpins tools like Fabric and Ansible's connection layer. In security and administration work it is used to automate post-exploitation actions on authorized hosts, mass-manage servers, transfer tooling, and — carefully — test SSH authentication controls.
pip install paramikoimport os
import paramiko
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.RejectPolicy()) # never AutoAddPolicy
try:
client.connect(
hostname="127.0.0.1",
username=os.environ["LAB_SSH_USER"],
key_filename=os.environ["LAB_SSH_KEY"],
timeout=10,
)
stdin, stdout, stderr = client.exec_command("id")
print(stdout.read().decode().strip())
print("exit status:", stdout.channel.recv_exit_status())
finally:
client.close()Credentials come from the environment, and unknown host keys are rejected rather than silently trusted.
| API | Purpose |
|---|---|
paramiko.SSHClient() |
High-level client |
client.load_system_host_keys() |
Load ~/.ssh/known_hosts |
client.set_missing_host_key_policy(policy) |
RejectPolicy (safe), WarningPolicy, AutoAddPolicy (unsafe) |
client.connect(hostname, username, key_filename=, password=, timeout=) |
Establish the session |
client.exec_command(cmd) |
Returns (stdin, stdout, stderr) |
stdout.channel.recv_exit_status() |
The command's exit code |
client.open_sftp() |
SFTP client for file transfer |
sftp.get(remote, local) / sftp.put(local, remote) |
Transfer files |
paramiko.RSAKey.from_private_key_file(path, password=) |
Load a key explicitly |
paramiko.AuthenticationException / SSHException |
Error types to catch |
client.close() |
Release the connection |
Run a command over SSH and capture output:
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect("192.168.56.10", username="tester", password="Passw0rd!", timeout=10)
stdin, stdout, stderr = client.exec_command("uname -a && id")
print(stdout.read().decode())
client.close()Linux target 6.1.0-amd64 #1 SMP x86_64 GNU/Linux
uid=1000(tester) gid=1000(tester) groups=1000(tester)
Transfer a file with SFTP:
import paramiko
transport = paramiko.Transport(("192.168.56.10", 22))
transport.connect(username="tester", password="Passw0rd!")
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.put("enum.sh", "/tmp/enum.sh") # upload a tool
sftp.get("/etc/hostname", "remote_hostname") # download evidence
print("files on /tmp:", sftp.listdir("/tmp"))
sftp.close()
transport.close()files on /tmp: ['enum.sh', 'systemd-private-...']
Authenticate with a private key instead of a password:
import paramiko
key = paramiko.RSAKey.from_private_key_file("id_rsa")
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect("192.168.56.10", username="tester", pkey=key)
_, stdout, _ = client.exec_command("hostname")
print("connected to:", stdout.read().decode().strip())
client.close()connected to: target
- SSH automation — run enumeration or configuration commands across many authorized hosts from one script.
- Post-exploitation tasking — after gaining valid credentials, script cleanup, evidence collection, or lateral movement on in-scope systems.
- Secure file transfer — push tooling and pull loot/evidence via SFTP without extra binaries on the target.
- Credential-control testing — validate key-vs-password policy and account lockout behaviour against systems you are authorized to test.
- Tunnelling / pivoting — build port forwards through a compromised jump host for reaching internal segments.
Warning
Use valid, authorized credentials only. Automated login attempts against systems you don't control can constitute unauthorized access.
- Using
AutoAddPolicy()— this accepts any host key, silently defeating the protection against man-in-the-middle attacks. - Ignoring the exit status —
exec_commandsucceeds even when the remote command fails; checkrecv_exit_status(). - Not reading stderr, so remote errors vanish.
- Deadlocking on large output — read
stdoutbefore waiting on the exit status. - Hardcoding passwords or key passphrases in the source.
- Forgetting
client.close(), leaking connections. - Omitting
timeout=, so an unresponsive host hangs the script. - Building remote commands by string interpolation — the remote shell interprets metacharacters, so this is command injection.
[!warning] Authorized use only Connect only to hosts you own or are explicitly authorized to administer or test.
AutoAddPolicyis the critical mistake here. It trusts whatever key the server presents, so an attacker who can intercept the connection can transparently proxy your session and capture credentials. UseRejectPolicyand manageknown_hostsdeliberately.- Never hardcode credentials. Read usernames, passwords, and key paths from environment variables or a secrets manager; a key committed to a repository must be treated as compromised.
- Prefer key authentication over passwords, and protect private keys with a passphrase and
0600permissions. - Remote command construction is injection-prone. Anything interpolated into
exec_command()reaches a shell — validate against an allowlist, or use SFTP and structured operations instead. - Sessions are privileged. A compromised automation host with stored keys is a path into every system it can reach; scope keys narrowly and use
command=restrictions inauthorized_keys. - Log connections and commands so automated administration remains auditable.
- Avoid
AutoAddPolicyin real assessments — useWarningPolicy()or pin known host keys to detect MITM. - Prefer key-based auth (
RSAKey/Ed25519Key) over embedding passwords in scripts. - Always set
timeout=and close clients/transports in atry/finally. - Read
stderras well asstdout; exit status is instdout.channel.recv_exit_status(). - Store credentials in environment variables or a secrets manager, never hard-coded.
- [[pwntools]] — its
sshtubes cover exploit-oriented SSH work - [[psutil]] — inventory the hosts you manage over SSH
- [[Readme|Python for Security Professionals]] — course home