The Handshake Doesn't Lie
HASSH fingerprint attribution across a banner-spoofed SSH client
Summary
Two IP addresses โ 89.248.168.227 (NL, AS202425) and 77.246.159.182 (RU, AS29182) โ share an identical SSH client fingerprint: HASSH c39f4cec145ee3d50fb590595143b9d5. The NL node declares SSH-2.0-Go; the RU node declares SSH-2.0-OpenSSH_7.4. Both produce the same HASSH. The version string from the RU node is a spoof. Both nodes run the same Go-based SSH client. The HASSH is unclassified โ it does not match any of the 68 known SSH client signatures in the honeypot database, including the three known Go-based scanner fingerprints. The client is private or custom-built.
Key Findings
- F1 โ HASSH cross-infrastructure link (SEVERITY: HIGH / CONFIDENCE: MEDIUM) โ 89.248.168.227 and 77.246.159.182 share HASSH
c39f4cec145ee3d50fb590595143b9d5, establishing shared SSH client toolkit across two geographically distinct nodes in different ASNs. - F3 โ Version string spoofing confirmed (SEVERITY: MEDIUM / CONFIDENCE: HIGH) โ 77.246.159.182 declares
SSH-2.0-OpenSSH_7.4while producing a Go SSH HASSH. The banner is a deliberate fabrication contradicted by the library-level fingerprint.
Section 1 โ What HASSH Is and Why It Cannot Be Spoofed
HASSH (Hash of All SSH Handshake) is a client fingerprinting method that operates at the library level of the SSH protocol stack, not at the identification layer. To understand why it is resistant to banner spoofing, the SSH connection sequence must be examined at the protocol level.
The Two Protocol Phases
SSH Connection Sequence โ Two Distinct Phases
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PHASE 1 โ Protocol Identification (RFC 4253 ยง4.2) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Timing: Immediately after TCP connection establishment โ
โ Direction: Both parties send simultaneously โ
โ Content: Plaintext string: SSH-protoversion-softwareversion โ
โ โ
โ Examples: โ
โ SSH-2.0-OpenSSH_7.4 โ declared by 77.246.159.182 (lie) โ
โ SSH-2.0-Go โ declared by 89.248.168.227 (truth) โ
โ โ
โ Authentication: NONE. Self-declared. Unverified by protocol. โ
โ Forensic value: VERY LOW โ trivially falsifiable โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ PHASE 2 โ Key Exchange Initialization (RFC 4253 ยง7.1) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Timing: Immediately after Phase 1 โ
โ Direction: Both parties send SSH_MSG_KEXINIT packets โ
โ Content: Ordered algorithm preference lists from SSH library โ
โ โ
โ Fields captured in HASSH computation: โ
โ [1] kex_algorithms โ
โ [2] encryption_algorithms_client_to_server โ
โ [3] mac_algorithms_client_to_server โ
โ [4] compression_algorithms_client_to_server โ
โ โ
โ These are set by the SSH LIBRARY compiled-in defaults โ
โ โ not by any configuration the banner string can influence. โ
โ Forensic value: HIGH โ library-level, not operator-configurable โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
KEY STRUCTURAL SEPARATION:
Phase 1 (banner) โโโ operator-controlled string โโโ FALSIFIABLE
Phase 2 (HASSH) โโโ library-compiled defaults โโโ NOT falsifiable
without recompiling the library
How HASSH Is Computed
HASSH Computation โ Step by Step
Step 1: Client sends SSH_MSG_KEXINIT during Phase 2
kex_algorithms = "curve25519-sha256,ecdh-sha2-nistp256,
diffie-hellman-group14-sha256,..."
encryption_algs_clientโsrv = "chacha20-poly1305@openssh.com,
aes128-ctr,aes256-ctr,..."
mac_algs_clientโserver = "hmac-sha2-256-etm@openssh.com,
hmac-sha1,..."
compression_algs_clientโsrv = "none,zlib@openssh.com"
Step 2: Concatenate four lists with semicolons
"curve25519-sha256,...;chacha20-poly1305@openssh.com,...;
hmac-sha2-256-etm,...;none,..."
Step 3: Compute MD5
MD5(concatenated_string) = c39f4cec145ee3d50fb590595143b9d5
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HASSH = MD5(alg_list_concat) โ
โ Source: SSH library compiled code โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The algorithm lists and their ordering are set at compile time
in the SSH library. Changing the banner string in Phase 1 has
zero effect on the algorithm lists transmitted in Phase 2.
To change the HASSH, an operator must:
(1) Recompile the SSH library with different algorithm defaults, OR
(2) Programmatically override algorithm preferences at runtime, OR
(3) Switch to a different SSH library entirely
None of these is achievable by banner string modification alone.
Forensic Meaning of HASSH Identity
When two observed connections share an identical HASSH, it establishes that both connections originated from the same SSH library build โ same language, same library, same compiled defaults. This is a property of the implementation, not of the operator's runtime choices. Different SSH implementations produce structurally different HMACs:
| SSH Implementation | HASSH Characteristics |
|---|---|
| OpenSSH (various versions) | OpenSSH-characteristic algorithm ordering; changes across major versions |
| Python Paramiko | Paramiko-characteristic defaults; distinctly different from OpenSSH |
| Go golang.org/x/crypto/ssh | Go-characteristic defaults; structurally distinct from both OpenSSH and Paramiko |
| libssh2 | libssh2-characteristic ordering; identifiable by cipher suite profile |
The central forensic claim: same HASSH equals same SSH client code. It does not establish same operator, same machine, or same campaign with certainty โ but it establishes same toolkit at the library level with high confidence.
Section 2 โ The Fingerprint: c39f4cec145ee3d50fb590595143b9d5
Direct Observations
The hash c39f4cec145ee3d50fb590595143b9d5 was observed on exactly two IPs in the honeypot database:
| IP Address | Country / ASN | Banner Declared | HASSH | Classification |
|---|---|---|---|---|
89.248.168.227 |
NL / AS202425 | SSH-2.0-Go |
c39f4cec145ee3d50fb590595143b9d5 |
UNCLASSIFIED |
77.246.159.182 |
RU / AS29182 | SSH-2.0-OpenSSH_7.4 โ |
c39f4cec145ee3d50fb590595143b9d5 |
UNCLASSIFIED |
The NL node declares SSH-2.0-Go, which is consistent with a Go SSH client. The HASSH corroborates this honest declaration. The RU node declares SSH-2.0-OpenSSH_7.4, which the HASSH directly contradicts. Both produce the identical fingerprint, meaning the same Go-based library produced both connections regardless of what the banner claims.
Population Context: Three Known Go SSH Scanners in the Honeypot Database
The honeypot fingerprint database contains 68 total SSH client signatures. Three of those belong to known Go-based SSH scanners. Comparing c39f4cec against those three reveals critical isolation:
| HASSH | Tool Name | IPs Observed | Status |
|---|---|---|---|
2aec6b44b06bec95d73f66b5d30cb69a |
go_scanner | 303 | Classified โ known public tool |
16443846184eafde36765c9bab2f4397 |
Go_SSH | 320 | Classified โ known public tool |
e54ef3ec27fe1fea7ab64d3fa05359fd |
go_scanner (variant) | 18 | Classified โ known public tool |
c39f4cec145ee3d50fb590595143b9d5 |
(none assigned) | 2 | UNCLASSIFIED โ this investigation |
Population Distribution โ What the IP Counts Mean
Commodity Go SSH scanners (publicly distributed tools):
go_scanner (2aec6b44) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 303 IPs
Go_SSH (16443846) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 320 IPs
go_scanner v2 (e54ef3ec) โโ 18 IPs
This investigation:
UNKNOWN (c39f4cec) โ 2 IPs
Interpretation:
Widely distributed scanner packages accumulate hundreds of hits.
Many actors deploy the same commodity tool across many IPs.
A count of 2 is inconsistent with public distribution.
The most parsimonious explanation:
โ A private or custom-built Go SSH client
โ Purpose-built for this actor's specific use case
โ Not publicly released or distributed widely
โ The algorithm preferences differ from all three known Go scanners
(distinct MD5 values = distinct algorithm list contents)
What "Unclassified" Means
The honeypot database records a tool_name field when a HASSH is matched against known signatures. For c39f4cec145ee3d50fb590595143b9d5, this field is empty. This means:
- The hash does not appear in any public signature database used to populate tool classifications
- It is not a variant of the three known Go scanner HMACs โ the MD5 values are distinct, meaning the underlying algorithm preference lists differ at the content level
- No previous investigation or automated classification pipeline has assigned a tool name to it
The population size (2 IPs) is consistent with a purpose-built tool used by a small number of nodes, as opposed to a widely distributed scanner package which would typically accumulate hundreds of hits from indiscriminate deployment across multiple actors.
Section 3 โ The Banner Spoofing Case
77.246.159.182 presents SSH-2.0-OpenSSH_7.4 as its version string on every SSH connection it initiates. The HASSH produced by the same IP is c39f4cec145ee3d50fb590595143b9d5 โ identical to the HASSH from 89.248.168.227, which declares SSH-2.0-Go.
A genuine OpenSSH 7.4 client would produce an OpenSSH-characteristic HASSH reflecting OpenSSH 7.4's algorithm preferences and ordering. It would not produce a Go SSH library HASSH. The two are structurally different because they originate from different language ecosystems with different compiled defaults. The conclusion is unambiguous: 77.246.159.182 is running a Go SSH client with a forged banner string.
Why OpenSSH 7.4 Specifically
OpenSSH 7.4 was released December 2016 and remained a common deployment through 2017โ2020 across RHEL/CentOS 7 and Debian Stretch systems. This specific selection is not arbitrary:
| Selection Criterion | Why It Works for the Operator |
|---|---|
| Plausibility as legacy server | Consistent with an "unpatched" internal tool, embedded system, or long-running VPS โ the standard OpenSSH version on CentOS 7 |
| Age profile (circa 2016โ2020) | Old enough for defenders to expect in legacy environments; not so old (โ OpenSSH 4.x) as to be immediately suspicious |
| Not a scanner banner string | Known scanner strings (SSH-2.0-Go, SSH-2.0-libssh2_*, SSH-2.0-python-paramiko_*) are blocklisted and IDS-flagged; OpenSSH_7.4 is not |
| CentOS 7 default | One of the most widely deployed server distributions of its era โ maximises the "this looks like a real host" effect in defender tooling |
| Reads as legitimate admin tool | An old OpenSSH version is suspicious for a public server but reads as plausible for internal scanning or legacy tooling โ reduces IDS alert priority |
The selection implies the operator understands how version strings are evaluated by defenders and honeypot operators. They chose a version that reads as plausible internal tooling rather than an active scanning tool. This is operational security awareness, not a default configuration.
What the Operator Knows โ and May Not Know
Operator Knowledge Assessment
WHAT THE OPERATOR DEMONSTRABLY KNOWS:
โ SSH banners are logged and analyzed by defenders
โ Declaring SSH-2.0-Go attracts classification as a scanner tool
โ SSH-2.0-OpenSSH_7.4 reads as a legitimate, if old, server
โ How to modify a Go SSH client to substitute the identification string
โ Which version string is sufficiently plausible to avoid suspicion
WHAT THE OPERATOR MAY NOT KNOW (or assessed as acceptable risk):
? That HASSH fingerprinting exists as a parallel identification layer
? That honeypots with HASSH collection will detect the contradiction
? That the HASSH links both IPs to the same toolkit regardless of banner
CONSEQUENCE:
The banner spoofing works against: โ banner-matching blocklists
โ IDS version-string signatures
โ analyst first-pass on version strings
The banner spoofing fails against: โ HASSH-aware honeypots
โ library-fingerprint analysis
โ cross-IP HASSH correlation
The banner is the lie.
The HASSH is the evidence.
NL Node vs. RU Node: Comparative Portrait
| Property | 89.248.168.227 (NL) | 77.246.159.182 (RU) |
|---|---|---|
| ASN / Location | AS202425 / Netherlands | AS29182 JSC IOT / Moscow, Russia |
| SSH Banner | SSH-2.0-Go | SSH-2.0-OpenSSH_7.4 |
| HASSH | c39f4cec145ee3d50fb590595143b9d5 | c39f4cec145ee3d50fb590595143b9d5 |
| Banner honest? | YES | NO โ active deception |
| Go client confirmed? | Yes (banner + HASSH agree) | Yes (HASSH contradicts banner) |
| PTR evasion observed? | Yes โ no-reverse-dns-configured.com | Not observed |
| Operational security posture | Transparent at banner layer; evasion at DNS infrastructure layer | Active deception at banner layer; no PTR evasion observed |
Two nodes sharing a HASSH apply different opsec decisions at different protocol layers. This divergence suggests either different operators using the same private tool with independent configurations, or a single operator applying different evasion strategies per node based on targeting environment. Neither interpretation can be confirmed from current evidence.
Section 4 โ Bidirectional Similarity Confirmation
The honeypot's similarity engine lists 77.246.159.182 and 89.248.168.227 as similar_ips of each other. The relationship is bidirectional: each IP appears in the other's similar_ips record. This is system-level confirmation that the engine identified the two IPs as closely related across multiple behavioral and fingerprint signals โ independently of and in addition to the HASSH match.
Bidirectional Link โ Why It Matters
89.248.168.227 โ similar_ips โ [77.246.159.182, ...] 77.246.159.182 โ similar_ips โ [89.248.168.227, ...] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ UNIDIRECTIONAL (AโB only): Occurs when one IP has sparse data. Asymmetric scoring can produce a single-direction match. Lower confidence. Possible false positive from data sparsity. BIDIRECTIONAL (AโB AND BโA): Both IPs had sufficient data for independent scoring. Both scores crossed the similarity threshold independently. STRONGER signal. Reduces probability of false positive. โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ The bidirectional listing was produced by the same system that maintains the HASSH database โ corroboration from a second, independent similarity calculation aggregating multiple signals.
The similarity engine uses behavioral and fingerprint signals including but not limited to: HASSH, credential sets used (usernames/passwords), command sequences executed in sessions, download URLs and payload hashes, temporal patterns (time-of-day, inter-connection intervals), geographic and network routing attributes, and port scanning sequences. The HASSH being identical is one of the stronger individual signals, because it is library-level and not easily varied between sessions. The bidirectional listing means the total weight of shared signals crossed the threshold from both directions.
Section 5 โ Evidential Limits
Forensic discipline requires explicit statement of what the HASSH evidence does not support.
| Claim | Supported? | Reason |
|---|---|---|
| 77.246.159.182 runs a Go SSH client | YES โ HIGH | HASSH identical to Go-declaring node; Go HASSH structurally distinct from OpenSSH |
| 77.246.159.182 spoofed its SSH banner | YES โ HIGH | Direct HASSH contradiction; no alternative explanation exists |
| Both IPs use the same SSH client code | YES โ HIGH | Identical HASSH + independent bidirectional similarity confirmation |
| Client is private/custom, not commodity | MEDIUM-HIGH | No match in 68-signature database; 2 IPs vs commodity range of 18โ320 |
| Both IPs operated by the same actor | MEDIUM | HASSH + behavioral similarity; shared-tool / different-operator scenario not excluded |
| Specific Go library version identified | NO | HASSH does not encode version metadata; no empirical testing against known builds performed |
| C2 coordination confirmed | NO | Toolkit similarity does not establish command-and-control relationship |
| Same physical operator confirmed | NO | An operator could distribute a compiled binary to affiliates; HASSH cannot distinguish this scenario |
Confidence Assessment
| Claim | Confidence | Primary Basis |
|---|---|---|
| 77.246.159.182 runs a Go SSH client | HIGH | HASSH match to Go-declaring node; structural incompatibility with any authentic OpenSSH HASSH |
| 77.246.159.182 spoofed its SSH banner | HIGH | Direct HASSH contradiction of declared banner; no alternative explanation is coherent |
| Both IPs use the same SSH client code | HIGH | Identical HASSH + independent bidirectional similarity confirmation from separate scoring system |
| Client is private / custom-built | MEDIUM-HIGH | No match in 68-signature database; population count of 2 vs 18โ320 for commodity tools |
| Both IPs operated by the same actor | MEDIUM | HASSH + behavioral similarity; shared-tool / different-operator scenario not excluded from evidence alone |
| Client uses Go's crypto/ssh library | MEDIUM | NL node's honest SSH-2.0-Go declaration; HASSH not matched to any known alternative library defaults |