TI-2026-057C ยท The Evasion Codex ยท Part III of IV

The Handshake Doesn't Lie

HASSH fingerprint attribution across a banner-spoofed SSH client

TI-2026-057C Series: The Evasion Codex Part 3 of 4 2026-06-30 TLP:WHITE

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

  1. 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.
  2. F3 โ€” Version string spoofing confirmed (SEVERITY: MEDIUM / CONFIDENCE: HIGH) โ€” 77.246.159.182 declares SSH-2.0-OpenSSH_7.4 while 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 ImplementationHASSH Characteristics
OpenSSH (various versions)OpenSSH-characteristic algorithm ordering; changes across major versions
Python ParamikoParamiko-characteristic defaults; distinctly different from OpenSSH
Go golang.org/x/crypto/sshGo-characteristic defaults; structurally distinct from both OpenSSH and Paramiko
libssh2libssh2-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 AddressCountry / ASNBanner DeclaredHASSHClassification
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:

HASSHTool NameIPs ObservedStatus
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 CriterionWhy It Works for the Operator
Plausibility as legacy serverConsistent 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 stringKnown 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 defaultOne 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 toolAn 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

Property89.248.168.227 (NL)77.246.159.182 (RU)
ASN / LocationAS202425 / NetherlandsAS29182 JSC IOT / Moscow, Russia
SSH BannerSSH-2.0-GoSSH-2.0-OpenSSH_7.4
HASSHc39f4cec145ee3d50fb590595143b9d5c39f4cec145ee3d50fb590595143b9d5
Banner honest?YESNO โ€” active deception
Go client confirmed?Yes (banner + HASSH agree)Yes (HASSH contradicts banner)
PTR evasion observed?Yes โ€” no-reverse-dns-configured.comNot observed
Operational security postureTransparent at banner layer; evasion at DNS infrastructure layerActive 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.

ClaimSupported?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

ClaimConfidencePrimary 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
โš  Personal capacity. Research published independently โ€” not reflecting employer views. Derived from passive observation of attacks against personal infrastructure. Full disclaimer โ†’
โ† Previous The Evasion Codex โ€” 3 / 8 Next โ†’