Lion Cipher Lab
Fingerprint SSH with HASSH and pivot
- Threat Intel Analyst
- Intermediate
- ~45 min
- Free · no account required
Why it matters
A great deal of threat-intelligence work is not about one malicious IP -- it is about finding every OTHER host the same attacker tool touched, so you can pre-empt it. On the SSH surface, HASSH is one of the cleanest ways in. SSH (Secure Shell, the encrypted remote-login and file-transfer protocol that runs on TCP port 22) begins every connection with a handshake, and the very first substantive message each side sends -- the SSH_MSG_KEXINIT -- travels UNENCRYPTED and lists, in a specific order, every algorithm that side supports: the key-exchange methods (how the two sides agree on a shared…
What you'll learn
- what HASSH is: an MD5 fingerprint of the algorithm lists an SSH client advertises in its unencrypted KEXINIT message -- the SSH-protocol cousin of JA3/JARM/JA4 for TLS
- the core distinction from the TLS labs: HASSH fingerprints the SSH handshake (KEXINIT), NOT the TLS handshake (ClientHello/ServerHello) -- a different protocol entirely
- the two halves: HASSH fingerprints the CLIENT (client-to-server lists); HASSHServer fingerprints the SERVER (server-to-client lists)
- generating your OWN SSH handshake traffic ethically -- an SSH server in Docker, ssh to it from the host -- and capturing it to a .pcap with tcpdump/Wireshark
- reading the KEXINIT out of a capture in Wireshark or with tshark (ssh.message_code == 20) and pulling the four algorithm-list fields
- computing a HASSH from scratch with Python's stdlib hashlib -- joining the four lists with semicolons per the Salesforce spec and md5()-hashing, no internet or paid API
- the pivot: a SECOND handshake from the same client to a different server yields the SAME HASSH even though the IP/port changed -- so you CLUSTER by implementation, not address
- the judgment: a COMMON HASSH (OpenSSH defaults, shared by millions of hosts) is weak attribution; a RARE/unusual HASSH (a custom client, an odd algorithm order) is high-signal
- the honest limits: HASSH is MD5 of an ordered list, so collisions across the default population are the norm; it fingerprints SOFTWARE, not a person; OpenSSH version drift (ext-info, strict-kex) shifts a HASSH across builds
- the pro workflow: pivot a HASSH across Zeek/EDR/proxy logs (or, optionally, a service like Shodan/Censys) to find every host an attacker's SSH tool touched -- always as one corroborating signal, never a lone verdict
Steps in this lab
- The idea: fingerprint the SSH handshake, not the address
- HASSH vs the TLS fingerprints -- the distinction that keeps this from being a JARM/JA4 rerun
- What is inside a KEXINIT, and exactly which fields feed the fingerprint
- Set the scope, then generate your OWN SSH handshake in Docker
- Read the KEXINIT out of the capture
- Compute the HASSH by hand -- a few lines of stdlib Python
- Pivot: a second handshake, and watch the fingerprint cluster (or not)
- The judgment: common vs rare, and what HASSH does NOT tell you
- What you built, and where it plugs in