Evading SSH Fingerprinting (HASSH) with Arbitrary Ciphers

Reading Time: 2 minutes

HASSH is an SSH fingerprinting method developed by Salesforce. HASSH allows SSH servers to fingerprint SSH clients attempting to connect by examining the client’s handshake data, such as which ciphers the client is asking to use, etc.
An inverse fingerprinting is also possible by using HASSHServer.

HASSH is a similar idea to JA3 / JA3S (also by Salesforce), whereas the fingerprinting is done against TLS handshakes.

At the end of the fingerprinting, a hash is generated which may or may not uniquely identify the client. For instance, an OpenSSH client may produce a hash value of A whereas a Dropbear SSH client may produce a hash of value B.

Why is this even useful? well, it’s a data point you can derive from the client. You might want to apply certain restrictions based on the client that’s connecting, or maybe you just want to analyze who’s connecting and using what technology.

When an SSH client connects to a server, it would typically provide the following:

  • Key Exchange Methods
  • Encryption
  • Message Auth
  • Compression

There are a list of elements SSH clients may send for each part of the handshake, e.g.

Key Exchange Methods: curve25519-sha256@libssh.org,ecdh-sha2-nistp384
Encryption: aes128-cbc,aes128-ctr,aes192-cbc
Message Authentication: hmac-sha1,hmac-sha1-96,hmac-md5,
Compression: zlib@openssh.com,zlib,none

One method to make it harder for servers to fingerprint clients is by mixing up / randomizing the order in which the client is offering these methods, example for encryption re-ordering:

aes128-cbc,aes128-ctr,aes192-cbc becomes aes128-ctr,aes192-cbc, aes128-cbc

This is all nice and dandy but I wanted something else. To start, I had a theoretical question:

Do SSH servers even care what the client provides for encryption types once there’s a mutual agreement on the first match?

Apparently, they don’t (at least OpenSSH doesn’t) and that somewhat makes sense, otherwise they would have to keep a list of all the possible ciphers and verify each one even if they have no plans to use it. What this means is, clients could do something such as providing the following ciphers upon connecting:
aes128-cbc, aes128-ctr, aes192-cbc, RANDOMSTRING

Here is how a client handshake looks like in Wireshark:

This makes fingerprinting clients way less reliable, and easier to evade.

For the purpose of the example, here is how you can use Python’s paramiko SSH library to randomize your ciphers using Transport._preferred_ciphers.

unique_algo = 'RANDOM_STRING'
paramiko.Transport._preferred_ciphers = ('aes128-ctr','aes192-ctr','aes256-ctr','aes128-cbc','aes192-cbc','aes256-cbc','blowfish-cbc','3des-cbc',unique_algo, )
client = paramiko.Transport(('', 22))
client.connect(username='test', password='test')
session = client.open_channel(kind='session')

Leave a Reply

Your email address will not be published. Required fields are marked *