sshdecode

package
v0.556.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package sshdecode parses SSH wire-protocol frames per RFC 4253 (SSH Transport Layer Protocol) and RFC 4250-4256. Specifically it covers the cleartext portions every SSH session emits before encryption is negotiated: the "SSH-2.0-..." version-exchange banner and the SSH_MSG_KEXINIT message that lists the algorithms each peer supports.

Wrap-vs-native judgement

Native. RFC 4253 §4.2 defines the version banner as plain ASCII; §6 defines the binary packet envelope; §7.1 defines KEXINIT. SSH name-lists are comma-separated UTF-8 strings. Pasting either form into this decoder is enough — no SSH client/server, no key material, no live network attach.

This is the SSH counterpart to tls_handshake_decode: the HASSH / HASSHServer fingerprint (Salesforce, ben-aaron-bowers, 2018) is the SSH analogue of JA3 and identifies SSH client/ server stacks across thousands of distinct signatures.

What this package covers

  • **Version exchange line** (RFC 4253 §4.2): `SSH-protoversion-softwareversion [SP comments]` — broken out into protocol version (1.x / 1.99 / 2.0), software version (OpenSSH_8.9p1 / dropbear_2022.83 / libssh2_1.10.0 / etc.), and optional comment field.
  • **Binary packet envelope** (RFC 4253 §6): `[packet_length:4][padding_length:1][payload][padding] [MAC]`. The packet length excludes the length field itself but includes padding_length, payload, and padding. MAC length is session-dependent so we surface it as raw trailing bytes (if any are present beyond the declared packet length).
  • **Message type dispatch** (27-entry table from RFC 4250 §4.1.2): SSH_MSG_DISCONNECT (1) / SSH_MSG_IGNORE (2) / SSH_MSG_UNIMPLEMENTED (3) / SSH_MSG_DEBUG (4) / SSH_MSG_SERVICE_REQUEST (5) / SSH_MSG_SERVICE_ACCEPT (6) / SSH_MSG_EXT_INFO (7) / SSH_MSG_NEWCOMPRESS (8) / SSH_MSG_KEXINIT (20) / SSH_MSG_NEWKEYS (21) / SSH_MSG_KEXDH_INIT (30) / SSH_MSG_KEXDH_REPLY (31) / SSH_MSG_USERAUTH_REQUEST (50) / SSH_MSG_USERAUTH_FAILURE (51) / SSH_MSG_USERAUTH_SUCCESS (52) / SSH_MSG_USERAUTH_BANNER (53) / SSH_MSG_USERAUTH_INFO_REQUEST (60) / SSH_MSG_USERAUTH_INFO_RESPONSE (61) / SSH_MSG_GLOBAL_REQUEST (80) / SSH_MSG_REQUEST_SUCCESS (81) / SSH_MSG_REQUEST_FAILURE (82) / SSH_MSG_CHANNEL_OPEN (90) / SSH_MSG_CHANNEL_OPEN_CONFIRMATION (91) / SSH_MSG_CHANNEL_OPEN_FAILURE (92) / SSH_MSG_CHANNEL_WINDOW_ADJUST (93) / SSH_MSG_CHANNEL_DATA (94) / SSH_MSG_CHANNEL_EXTENDED_DATA (95) / SSH_MSG_CHANNEL_EOF (96) / SSH_MSG_CHANNEL_CLOSE (97) / SSH_MSG_CHANNEL_REQUEST (98) / SSH_MSG_CHANNEL_SUCCESS (99) / SSH_MSG_CHANNEL_FAILURE (100).
  • **SSH_MSG_KEXINIT decode** (RFC 4253 §7.1): 16-byte cookie + 10 name-lists (kex_algorithms, server_host_key_algorithms, encryption_algorithms_c2s, encryption_algorithms_s2c, mac_algorithms_c2s, mac_algorithms_s2c, compression_algorithms_c2s, compression_algorithms_s2c, languages_c2s, languages_s2c) + first_kex_packet_follows + 4-byte reserved.
  • **HASSH / HASSHServer fingerprints** (Salesforce spec): the colon-separated string `kex_algos;encryption_algos;mac_algos;compression_algos` using c2s lists for HASSH and s2c lists for HASSHServer, plus the MD5 hash of each. The HASSH client fingerprint identifies the SSH client stack (OpenSSH version, PuTTY, libssh, JSch, ParamPro, etc.) across thousands of distinct signatures.

What this package does NOT cover (deliberately out of scope)

  • Encrypted body decode for post-KEXINIT packets: SSH_MSG_USERAUTH_*, SSH_MSG_CHANNEL_*, and friends are sent over the encrypted session. The envelope is decoded but the body is surfaced as raw hex.
  • SSH-1 protocol (deprecated since ~2006) — banner parsing still works (the protocol-version field is surfaced), but the binary-packet path assumes SSH-2.
  • HASSH version of JA4 family (JA4SSH from FoxIO, 2023) — different algorithm; deferred until real-world demand surfaces.
  • Host-key extraction from SSH_MSG_KEXDH_REPLY — the RSA/ECDSA/Ed25519 public key blob is surfaced as hex but not parsed into structured form (that's a separate ASN.1 walker effort).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BinaryPacket

type BinaryPacket struct {
	PacketLength   int      `json:"packet_length"`
	PaddingLength  int      `json:"padding_length"`
	PayloadLength  int      `json:"payload_length"`
	MessageType    int      `json:"message_type"`
	MessageName    string   `json:"message_name"`
	PaddingHex     string   `json:"padding_hex,omitempty"`
	TrailingMACHex string   `json:"trailing_mac_hex,omitempty"`
	PayloadBodyHex string   `json:"payload_body_hex,omitempty"`
	KEXInit        *KEXInit `json:"kex_init,omitempty"`
}

BinaryPacket is the decoded SSH binary-packet envelope plus the dispatched body.

type Frame

type Frame struct {
	HexInput      string         `json:"hex_input,omitempty"`
	VersionBanner *VersionBanner `json:"version_banner,omitempty"`
	BinaryPacket  *BinaryPacket  `json:"binary_packet,omitempty"`
}

Frame is the decoded view of an SSH frame. Exactly one of VersionBanner or BinaryPacket is non-nil per call.

func Decode

func Decode(input string) (*Frame, error)

Decode parses an SSH frame. Input starting with `SSH-` is treated as the version banner; anything else is parsed as a hex-encoded binary packet.

func DecodeBytes

func DecodeBytes(b []byte) (*Frame, error)

DecodeBytes parses a raw SSH binary-packet byte slice.

type KEXInit

type KEXInit struct {
	CookieHex                           string   `json:"cookie_hex"`
	KexAlgorithms                       []string `json:"kex_algorithms"`
	ServerHostKeyAlgorithms             []string `json:"server_host_key_algorithms"`
	EncryptionAlgorithmsClientToServer  []string `json:"encryption_algorithms_client_to_server"`
	EncryptionAlgorithmsServerToClient  []string `json:"encryption_algorithms_server_to_client"`
	MACAlgorithmsClientToServer         []string `json:"mac_algorithms_client_to_server"`
	MACAlgorithmsServerToClient         []string `json:"mac_algorithms_server_to_client"`
	CompressionAlgorithmsClientToServer []string `json:"compression_algorithms_client_to_server"`
	CompressionAlgorithmsServerToClient []string `json:"compression_algorithms_server_to_client"`
	LanguagesClientToServer             []string `json:"languages_client_to_server"`
	LanguagesServerToClient             []string `json:"languages_server_to_client"`
	FirstKexPacketFollows               bool     `json:"first_kex_packet_follows"`
	Reserved                            uint32   `json:"reserved"`
	HASSH                               string   `json:"hassh"`
	HASSHHash                           string   `json:"hassh_hash"`
	HASSHServer                         string   `json:"hassh_server"`
	HASSHServerHash                     string   `json:"hassh_server_hash"`
}

KEXInit is the SSH_MSG_KEXINIT body.

type VersionBanner

type VersionBanner struct {
	Raw             string `json:"raw"`
	ProtocolVersion string `json:"protocol_version"`
	SoftwareVersion string `json:"software_version"`
	Comment         string `json:"comment,omitempty"`
}

VersionBanner is the decoded `SSH-...` text exchange.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL