martin carpenter


most popular
2012/05/05, updated 2012/12/15
ubuntu unity lens for vim



tags: ssh timing

github home
repository URLs

time_ssh_auth records the time for SSH authentication to fail against a host. Many SSH daemon implementations vary in the time taken to reject a valid user with invalid credentials compared to the time taken to reject an invalid (unknown) user. That gives us a way to enumerate usernames.

If you're thinking of using this for black-hatting then you will get noticed: this technique is really noisy. However if you're a white hat with a black box on your desk then this might be useful.

why is this interesting?

Vendors of "appliances" tend to be fussy about not-yet-customers peeling off "removal voids your warranty" stickers from loaner boxes and pulling out the disks. Your organization might be fussy about such excavation once you've bought the kit. How can you get a list of system users as part of your security evaluation?

Often such appliances are white-badged servers running some free UNIX variant. Sometimes, they use the system authentication system (PAM) for web-GUI/application authentication (perhaps especially where a command-line interface (ssh or telnet) is also available). Enumerating system usernames by timing system authentication failures might then give you application usernames. (Alternatively, can application credentials be used for system level access?).

Passwords are also theoretically obtainable by performing character-by-character timing comparisons (due to poorly implemented "short circuit" comparison or hash functions). This requires an exponential increase in computation (time) and more advanced statistical analysis.


An early cut in Python/Paramiko produced surprisingly good results against a PBX. I rewrote it in C and libssh for speed and to use real-time clock facilities.

The program attempts a given number of authentications with a given fixed password (-p) or identity file (-i). (If the authentication actually succeeds then that's perhaps the end of your evaluation...). The time for each authentication attempt is output in nanoseconds and at the end of the run the median average is computed for each user. We use the median since the mean can be adversely affected by a small number of significant outliers (eg. network glitch or CPU spike). The median is less susceptible to noise (statisticians say it is more robust).

If you are lucky there will be two distinct groups of timings: valid and invalid users. You probably know at least one valid user (eg. root) and your test should include usernames that are extremely unlikely to exist on the target.


sample results

The following results were obtained against a Western Digital MyBook, World Edition (a cheap Linux-on-ARM "home SAN") with 1000 password login attempts per user:

$ ./sshtime -p password mybook 1000 daemon bin sync operator \
nobody default guest root martin bogus1 bogus2 bogus3 bogus4 \
bogus5 >mybook.out 2>mybook.err
$ fgrep median mybook.out | sort -n -k3 
daemon           median  27426402
bin              median  27536087
sync             median  27659413
operator         median  27904556
nobody           median  28006044
bogus4           median  32301348
bogus5           median  32311740
bogus3           median  32322276
bogus2           median  32340496
bogus1           median  32354843
default          median  32690818
guest            median  32723860
root             median  46010003
martin           median  47073363

For reference here is the shadow(4) file (hashes elided):

# cat /etc/shadow

And some conclusions:

The last point remains a mystery. The GNU/Debian Linux man-page for shadow(4) only suggests that these characters are invalid and that invalid strings will preclude the user from logon.

If the password field contains some string that is not valid result of crypt(3), for instance ! or *, the user will not be able to use a unix password to log in, subject to pam(7).
This is supported by the source code, modules/pam_unix/passverify.c in PAM 1.0.1:
    } else if (!p || *hash == '*' || *hash == '!') {
            retval = PAM_AUTH_ERR;
I suspect that this behaviour is peculiar to the MyBook's Linux distribution.


It's a little surprising that the obvious countermeasure — to make the authentication step always take exactly the same amount of time whether it passes or fails — is not implemented in most (any?) SSH servers. But the risk here is tiny: the attack is fantastically noisy and the most that an attacker can reasonably gain is a list of valid usernames which might in any case be public or observable by other simpler means.

Other countermeasures are more widely deployed:

Of those, the first is by far the most prolific and such an attack is therefore mostly likely to provoke a DoS in short order.


Crosby, Wallach, & Riedi, Opportunities and Limits of Remote Timing Attacks, ACM Trans. Inf. Syst. Secur. 12, 3, Article 17 (January 2009).