Cobalt Strike remains the dominant C2 framework among ransomware affiliates and nation-state actors despite being a commercial penetration testing tool. Its default TLS configuration produces a stable JARM fingerprint that has been incorporated into every major threat intelligence blocklist since 2020. In 2026, operators are systematically rotating malleable C2 profiles to generate different JARM and JA3S fingerprints on a per-campaign basis, effectively defeating fingerprint-based network blocking. This report documents the rotation patterns observed, the countermeasures that work, and the ones that don't.
//JARM Fingerprinting Background
JARM, developed by Salesforce researchers in 2020, fingerprints TLS servers by sending 10 specially crafted ClientHello packets that probe different aspects of the server's TLS implementation: cipher suite preferences, extension handling, session resumption behaviour, and version negotiation. The server's responses are hashed into a 62-character fingerprint that is highly stable for a given TLS implementation and configuration.
Default Cobalt Strike 4.x listeners use the Java TLS stack with no custom certificate or cipher configuration, producing a well-known JARM fingerprint. This fingerprint has been added to Suricata rule sets, Palo Alto Wildfire, and commercial threat intel feeds.
JA3S Fingerprinting
JA3S fingerprints the TLS server from the ServerHello response: TLS version, chosen cipher suite, and extensions present in the server response. Like JARM, default Cobalt Strike produces a stable JA3S fingerprint. Unlike JARM (which requires active probing), JA3S is passively collectible from any position in the network with TLS visibility.
//Profile Rotation Patterns
In 2026, operators rotate malleable C2 profiles on a per-campaign or per-week cadence to change the TLS fingerprint before it can be added to blocklists. Three rotation strategies have been observed.
Strategy 1: Cipher Suite Reordering
The simplest rotation changes only the order of cipher suites in the TLS ClientHello (from the beacon side) and the server's preference list (via malleable profile ssl-cert block). Since JARM uses cipher suite preferences as a key input, reordering completely changes the fingerprint with minimal operational effort.
# Malleable C2 profile: cipher suite rotation example
# Profile A (week 1)
https-certificate {
set cipher_suite "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";
}
# JARM fingerprint A: 2ad2ad0002ad2ad00042d42d0000002ad2ad...
# Profile B (week 2) - same certs, different suite order
https-certificate {
set cipher_suite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384";
}
# JARM fingerprint B: 07d14d16d21d21d07c42d43d000000f5...
# Different fingerprint, same underlying C2, same beaconStrategy 2: CDN Proxying
A growing proportion of Cobalt Strike infrastructure routes beacon traffic through Cloudflare or other CDN providers using domain fronting or standard CDN proxying. The beacon communicates with a Cloudflare IP, and Cloudflare proxies requests to the actual C2 backend. The JARM fingerprint collected from the Cloudflare IP is Cloudflare's fingerprint, not the C2's, effectively hiding the C2 behind a legitimate infrastructure provider.
Strategy 3: Self-Hosted JVM TLS Customisation
Operators with technical sophistication are rebuilding the Cobalt Strike team server with custom JVM TLS configurations (JSSE provider substitution) to produce arbitrary JARM fingerprints. This requires modifying the team server JAR but is documented in operator communities and GitHub repositories.
//Countermeasures That Work
Since TLS-level fingerprinting is defeated by configuration changes, durable detection must operate on behavioural patterns that are harder to modify.
Beacon Timing Analysis
Cobalt Strike beacons have configurable sleep intervals with jitter. Even with jitter, the statistical distribution of inter-connection intervals is characteristic. Beacons with 60-second sleep + 20% jitter produce connections in the 48-72 second range. Over 20+ connections, the distribution is identifiable even when individual intervals overlap with legitimate traffic patterns.
# Beacon timing fingerprint (Zeek/Bro analysis)
# Look for periodic HTTPS connections with consistent interval distribution
event connection_established(c: connection) {
if (c$resp$port == 443 && is_external_ip(c$resp$h)) {
record_connection_timing(c$id$orig_h, c$resp$h, network_time());
}
}
# Post-hoc analysis: for each (src, dst) pair with >10 connections,
# compute coefficient of variation of inter-arrival times
# CS beacon with 20% jitter: CV typically 0.12-0.22
# Legitimate browser traffic: CV typically > 0.5HTTP Host Header Patterns
Malleable C2 profiles that mimic CDN or software update traffic produce distinctive Host header values that can be fingerprinted: the Host header value does not match the SNI in the TLS handshake (a common domain fronting tell), or the Host header follows a fixed pattern across all beacon connections regardless of the claimed content type.
2ad2ad0002ad2ad00042d42d0000002ad2ad2ad2ad2ad2ad2ad2ad2ad2ad2ad207d14d16d21d21d07c42d43d000000f50d155305214cf247147c43c0888a529d29d00029d29d00041d41d000000aa99ce74e2c6d013c745aa52b5cc042dDefault CS listener JARM (still observed on unpatched operators):
07d14d16d21d21d07c07d14d07d21d9b2f70a47ccd78d39cb2f6c70a47ccd78Beacon check-in HTTP response size range (consistent across profiles):
1250-1380 bytes (Content-Length + headers)