OPEN BETA
OPEN BETA
AKTIVES TLS-FINGERPRINTING

JARM Fingerprinting

C2-Server, Malware-Infrastruktur und TLS-Implementierungs-Cluster identifizieren — durch 10 speziell konstruierte ClientHello-Probes und Hashing der TLS-Negotiation-Antworten des Servers.

10Aktive Probes
62 ZeichenHash-Länge
TLS 1.0–1.3Alle Versionen
~150 msScan-Zeit pro Host
Passiv+Kein Payload

// Was JARM erkennt

JARM wurde von Salesforce Engineering (2020) entwickelt, um TLS-Server-Implementierungen zu fingerprinting — indem ausgenutzt wird, dass verschiedene TLS-Stacks auf dieselbe Menge von absichtlich missgestalteten oder ungewöhnlichen ClientHello-Nachrichten unterschiedlich reagieren. Der daraus resultierende 62-Zeichen-Hash ist deterministisch pro TLS-Stack-Version und Konfiguration — was ihn zum effektiven Signal für C2-Infrastruktur-Clustering macht, auch wenn sich IP-Adressen rotieren.

C2-Framework-Erkennung

  • Cobalt Strike — markanter Java-TLS-Fingerprint
  • Metasploit — spezifische OpenSSL-Defaults
  • Sliver / Havoc / Brute Ratel — bekannte Hashes von Salesforce Research veröffentlicht
  • AsyncRAT / njRAT — .NET TLS Stack Signaturen
  • Custom C2 auf nginx/Apache — matcht bekannte Server-Builds

Clustering-Anwendungsfälle

  • Alle Hosts mit gleichem TLS-Stack im Tenant-IP-Raum identifizieren
  • Von demselben Operator verwaltete Infrastruktur erkennen (Shared Hosting, CDN-Bypass)
  • Neu entdeckte Subdomains mit bekannt-sicheren oder bekannt-bösartigen Fingerprints korrelieren
  • TLS-Implementierungs-Drift nach Server-Upgrades tracken

Einschränkungen

  • Aktive Probe — erzeugt einen TLS-Handshake-Versuch auf jedem Ziel-Port
  • Umgeht keine Load Balancer, die TLS vor dem Backend terminieren
  • Rotierende TLS-Bibliotheken oder custom OpenSSL-Patches ändern den Hash
  • Kann ohne Known-Hash-Datenbank nicht zwischen identischen TLS-Stacks auf legitimer vs. bösartiger Infrastruktur unterscheiden

// Die 10-Probe-Sequenz

Jede Probe sendet ein konstruiertes TLS ClientHello mit einer spezifischen Kombination aus TLS-Version, Cipher-Suite-Reihenfolge und Extension-Set. Die ServerHello-Antwort des Servers — konkret welche Cipher gewählt wurde, welche TLS-Version akzeptiert wurde und welche Extensions zurückgespiegelt wurden — wird aufgezeichnet und zum Aufbau des Fingerprints verwendet.

#TLS-VersionCipher-ReihenfolgeExtensionsSNI
1TLS 1.2Vorwärts (stärkste zuerst)Alle unterstütztenVorhanden
2TLS 1.2Rückwärts (schwächste zuerst)Alle unterstütztenVorhanden
3TLS 1.2VorwärtsKeine ExtensionsFehlt
4TLS 1.1VorwärtsAlle unterstütztenVorhanden
5TLS 1.3Nur TLS 1.3-SuitesAlle unterstütztenVorhanden
6TLS 1.3TLS 1.3-Suites + 1.2 angehängtAlle unterstütztenVorhanden
7TLS 1.2GREASE-Werte injiziertGREASE ExtensionsVorhanden
8TLS 1.2VorwärtsNur max_fragment_lengthVorhanden
9TLS 1.0Export-Grade-Ciphers zuerstAlle unterstütztenFehlt
10SSLv3ALLE (inkl. Null-Ciphers)KeineFehlt

// Hash-Konstruktion

Jede Probe-Antwort wird auf ein Segment fester Breite reduziert. Die 10 Segmente werden konkateniert und SHA-256-gehasht, wobei die ersten 30 Bytes als "fuzzy"-Komponente und 2 Bytes als "exact"-Komponente behalten werden. Der finale 62-Zeichen-Hex-String ist der JARM-Hash.

Beispiel-JARM-Hash mit Segment-Aufschlüsselung (Cobalt Strike 4.x auf Java 11):

2ad2ad0002ad2ad000002ad2adffff00000000000000000000000000
TLS-Version-Segment   Gewählte Cipher   Extension-Echo   Keine Antwort / ALERT
def _compute_jarm(responses: list[ProbeResponse]) -> str: fuzzy_segments = [] for resp in responses: if resp.alert or resp.timeout: fuzzy_segments.append("|||") continue seg = "|".join([ resp.tls_version_selected, resp.cipher_selected, ",".join(resp.extensions_echoed), ]) fuzzy_segments.append(seg) raw = ",".join(fuzzy_segments) sha = hashlib.sha256(raw.encode()).hexdigest() # Erste 30 Bytes = Fuzzy-Hash; letzte 2 Bytes = Exact-Hash return sha[:30] + sha[30:62]

// Bekannte C2-Hash-Datenbank

YADS enthält eine kuratierte Datenbank bekannter bösartiger JARM-Hashes aus öffentlichen Threat-Intelligence-Quellen. Jeder Treffer löst einen SecurityFinding mit Schweregrad High aus — mit dem erkannten C2-Framework als Bezeichnung.

JARM-Hash (gekürzt)Erkanntes FrameworkVersionsbereichQuelle
2ad2ad0002ad...Cobalt Strike3.x – 4.4Salesforce Research
07d14d16d21d...Metasploit msfvenom5.x – 6.xJARM GitHub
00000000000000...TLS-Handshake verweigert / TimeoutN/ABerechnet
29d29d00029d...AsyncRAT / njRAT (.NET TLS)0.5.7+Unit 42
15d15d00000000...Sliver C2 (Go TLS)1.4+BishopFox Research
3fd3fd0003fd...Brute Ratel C40.9+Palo Alto Networks

// Integration in die YADS-Scanner-Pipeline

EigenschaftWert
Modulnamejarm_scanner
Scan-GruppeGruppe B (aktives Probing)
Standard-Ports443, 8443, 8080, 4443, 9443
Timeout pro Probe3 s TCP-Connect + 5 s Read
Gespeicherter OutputScanResult.data.jarm_hash (62 Zeichen), .jarm_match (Framework oder null), .jarm_ports (Dict Port→Hash)
Change-DetectionHash über alle Port→JARM-Paare; Finding wird wiedereröffnet wenn Hash sich ändert
Finding-SchweregradHigh bei bekanntem Treffer; Info sonst
YADS KB-ArtikelYF-JARM-001 — JARM C2 Infrastructure Match