Introducing JA4Scan: Active Server Fingerprinting for TLS and QUIC
John Althouse and Sébastien Féry
TL;DR
JA4Scan-TLS and JA4Scan-QUIC are our latest additions to the JA4+ suite of fingerprinting methods. Together they fingerprint the TLS and QUIC libraries running on any given server, uncovering both the running TLS/QUIC stack and its configuration.
You can think of JA4Scan as the modern replacement for JARM, from the same person. It can be used to:
Quickly verify that all servers in a group have the same TLS configuration.
Group disparate servers on the internet by configuration.
Identify default applications or infrastructure.
Identify malware command and control infrastructure and other malicious servers on the Internet.
JA4Scan is currently integrated in Security Scorecard's Driftnet.io, Hunt.io (Oct 1), MODAT and coming soon, if not already implemented, in Censys, Palo Alto Networks' Cortex Xpanse, Validin, and other EASM tools.
Background
JARM

Back in 2020, myself (John Althouse) and my team at Salesforce, Andrew Smart, RJ Nunnally, and Mike Brady, along with a bunch of work by Caleb Yu, created JARM, the active TLS server fingerprinting tool used throughout the industry today. And it worked great. We’ve been able to classify and track server infrastructure, including C2s, across the internet for years. But we made JARM for a TLS 1.2 world, a world where QUIC (HTTP/3) was still experimental and not officially released.

With TLS 1.3 over TCP and QUIC now accounting for the vast majority of the internet, JARM has been struggling. For example, when I searched for the JARM fingerprint of all 00s on hunt.io, it came back with over 74M results. That means the server is running TLS, the JARM scan ran, but returned no results. We found similar results on Censys and Driftnet.

In cases where there are results, the JARM fingerprints started to collide by a large amount, reducing their effectiveness.
What changed?
JARM was built entirely around the TLS 1.0-1.2 Server Hello packet. In TLS 1.3 a number of the signals are no longer in the Server Hello which now contain the bare minimum. The most segmenting signals have moved to encrypted extensions or to post-handshake messages and JARM does not look that deep. JARM was also only focused on TLS over TCP and could not scan QUIC servers (TLS over UDP), which now accounts for nearly half of all internet traffic.
It’s time for an upgrade. JA4Scan is that upgrade.
How JA4Scan Works
JA4Scan sends 17 probes to a target over both TCP and QUIC, across TLS 1.2, 1.3 and QUIC. Each probe is a specifically crafted Client Hello to elicit differentiating behavior from the server. The goal is not only to record the values a server chooses, but how it behaves: structural decisions hardcoded in the library that remain stable regardless of hardware and configuration.
The Fingerprints
Just like the rest of JA4+, JA4Scan fingerprints are meant to be human readable as well as machine usable with delimited sections allowing you to pivot on individual sections or the whole fingerprint. JA4Scan-TLS fingerprints traditional TLS over TCP servers, while JA4Scan-QUIC fingerprints QUIC servers. In the event that a server is running a dual stack, the combined JA4Scan-TLS + JA4Scan-QUIC becomes unique to that stack.


Examples:
Dual stacks
Site | JA4Scan-TLS | JA4Scan-QUIC | Stack |
Cloudflare | 23000h0s02s0_7c13a278e8c0_000000f0ac40_sd1c00299504 | 0300chlss200_8960528bd01f_000000b36f05_sd1000e9c647 | boringssl + quiche (cloudflare version) |
23000h0s02s0_865584d8a6d5_000000f703cf_sd1c00299504 | 031gchlsr000_bd327ffee725_6d6a17c51f1f_se1000e9c647 | boringssl + quiche (google version) | |
Fastly | 23100h0s04s0_093a61f6842f_8ba218cede0d_sr2sssc389dc | 03100hlss400_db6b5a8b860e_6d6a17001638_sr2sss473b09 | picotls + quicly |
Akamai | 23100h0s02sn_288488037518_fa4cedcede0d_0d1cs0a10eaa | 030g0hlsr20n_febdf6a4d8b3_24d9b60f20b7_0d10s05771b8 | boringssl + assumed akaquic |
Just like with the rest of JA4+, if a deployment differs in only one aspect, a partial fingerprint remains usable. In these examples the ciphers chosen by the servers were different, however, the first three components of the JA4Scan fingerprint remain unaffected. This allows for pivoting even when certain aspects are manually changed.
OpenSSL Version | Certificate Type | JA4Scan-TLS |
OpenSSL 3.5.5 | RSA | TLS=23000h0s02sn_f6f9e45b3dd5_1b98decede0d_sr1sssbe8207 |
OpenSSL 3.5.5 | ECDSA | TLS=23000h0s02sn_f6f9e45b3dd5_1b98decede0d_se1ss0651732 |
quic-go 0.61.0 | RSA | QUIC=031g0hlss100_5973261134f6_6d6a1784efde_sr10sc7b63be |
quic-go 0.61.0 | ECDSA | QUIC=031g0hlss100_5973261134f6_6d6a1784efde_se10s05444e9 |
Translating Fingerprints to Libraries
Clustering is useful on its own, but the objective is attribution. So we built JA4Scan-translate, a rule engine sidecar that maps JA4Scan runs to named TLS libraries and QUIC implementations, even with different configurations, the stacks used are still identifiable.

For example, Apple’s QUIC stack advertises two transport parameters not found in any RFC: 0xbabf and 0xff080808 - 0xbabf is enable_multipath - the experimental codepoint from the IETF multipath draft (still active at draft-21) that Apple adopted early and never updated. 0xff080808 is migration_version, an Apple-only parameter for client-initiated connection migration. Together they make Apple's QUIC implementation immediately identifiable on the wire. Rules like this are built into our JA4Scan-translate tool.
Identifying Infrastructure with Combined Fingerprints
The “mitmproxy” network interception tool leverages pyssl (OpenSSL) for TCP and since version 11, uses aioquic for QUIC support.
This results in the following fingerprints:
JA4Scan-TLS | 23000h0s02sn_6a10a6d46e59_1b98decede0d_sr1sssbe8207 |
JA4Scan-QUIC | 0310000ss000_2a673bc4abf8_aee409e4882a_sr2s0seb0750 |
This combination of stacks are unique, making the combination of JA4Scan-TLS AND JA4Scan-QUIC fingerprints unique to the tool.
Selective servers
Depending on their setup, TLS servers can admit a request based on SNI (domain name, common for reverse proxies) or ALPN (protocol-specific). If a request does not have the necessary domain name in the SNI or the correct ALPN, the server may refuse the connection.
JA4Scan-TLS and JA4Scan-QUIC record the shape of the refusal. This means that even if a server refuses the scanning probes, JA4Scan will still produce a pivotable fingerprint for that infrastructure and configuration.
SNI-gating:
Correct SNI | 23200h0s02sn_82383ca4b028_45f45ecede0d_0r1cscbd627c |
Wrong / No SNI | 0010000s0000_904985729b75_06203a000000_000000000000 |
nginx 1.27.5 / OpenSSL, ssl_reject_handshake on
Correct SNI | 23200h0s01s0_26ec5e416bec_d10823cede0d_0r1csca57cca |
Wrong / No SNI | 03100h0s00s0_070958e7790e_db7b01d72666_00000040811f |
Caddy 2.11.4 / Go crypto/tls, default
Correct SNI | 23100h0s01s0_26ec5e416bec_0c479acede0d_001cs0bd627c |
Wrong / No SNI | 03100h0s00s0_070958e7790e_65cd35d72666_00000040811f |
Traefik 2.11.54 / Go crypto/tls, sniStrict: true
Note that despite using the same runtime, the error profiles of Caddy and Traefik differ, yielding the same structural JA4Scan fingerprint with differing c sections.
ALPN-gating:
DOQ, DNS over QUIC, listeners gate on ALPN, they accept only “doq” in the Application Protocol Negotiation extension and refuse “h3”. This leads to unique JA4Scan-QUIC fingerprints.
Implementation | Stack | JA4Scan-QUIC |
Dnsproxy | Go, quic-go | 032g0hlss100_93b413a26a43_1165e672744b_0r10s04e82ca |
sdns | Go, quic-go | 032g0hlss100_93b413a26a43_1165e672744b_0r10s04e82ca |
mosdns | Go, quic-go | 032g0hlss100_93b413a26a43_1165e672744b_0r10s04e82ca |
Resisting Evasion - Sliver C2 Detection
Offensive tooling increasingly ships fingerprint-evasion features. Sliver, BishopFox’s C2 framework, offers a randomization option on its HTTPS listener that shuffles the server's cipher suite list and lowers the minimum TLS version on each start.
Four independent randomizations, each producing a different server configuration:
Config | JA4Scan-TLS |
TLS 1.1, 18 ciphers | 23100h0s01s0_10ec34d82fca_6d6a17cede0d_se0cs0b920bb |
TLS 1.1, 19 ciphers | 23100h0s01s0_10ec34d82fca_6d6a17cede0d_se0ss09c7d1d |
TLS 1.1, 20 ciphers | 23100h0s01s0_10ec34d82fca_6d6a17cede0d_se0cs0b920bb |
TLS 1.1, 16 ciphers | 23100h0s01s0_10ec34d82fca_6d6a17cede0d_se0cs0b4d286 |
TLS 1.0, 20 ciphers | 23100h0s01s0_10ec34d82fca_6d6a17cede0d_se0cs0b920bb |
Notice the first three components are identical across all six, including the configuration that dropped to TLS 1.0
Sliver's mutual-TLS listener carries this comment:
The configuration is TLS 1.3-only with RequireAndVerifyClientCert which produces this fingerprint:
At the time of writing, filtering that fingerprint to Sliver's default mTLS port, port 8888, narrows a broad configuration class down to only 59 hosts.
Diving into these on Hunt.io and some are very obviously Sliver with port 31337 also open and clearly showing all the signs of a Sliver C2. For example: 35.212.172.98

While others do not wave the Sliver flag so overtly. For example: 67.207.95.215

We can still confirm that these are Sliver C2s by matching the JA4X fingerprint. JA4X is a fingerprint of the X509 certificate structure, essentially fingerprinting the tool which generated the certificate and not the certificate details themselves. Known Sliver mTLS X509 certificates have the following fingerprints:
With this additional piece of pivot information, we can now expand our search beyond the known Sliver port of 8888 and find the really interesting IPs.
These IPs and ports have been independently validated by Hunt.io as either confirmed Sliver C2s or high confidence.
While these fingerprints, in isolation, collide with benign servers, in combination they become… sliver bullets... I’m sorry, I’m a dad. My point is that JA4+ fingerprints are designed to be used in combination as that is the key to high fidelity detection. So whenever you encounter malicious servers with self-signed certificates, I especially encourage you to combine JA4Scan + JA4X to find more of the same infrastructure.
This is just an example and none of this is meant to bash on Sliver or my friends at BishopFox. If you’re in the market for a Red Team engagement, I recommend you check them out!
Interesting QUIC Fingerprints
Datagrams
Implementation | Stack | JA4Scan-QUIC |
hysteria | Go, quic-go fork | 032g0hlss100_52347523195b_d1082384efde_0e1cs05444e9 |
gomoqt | Go, quic-go | 031g0hlss000_5973261134f6_6d6a17ca3fbe_se1cs05444e9 |
moq-relay | Rust, quinn / rustls | 031g0hlsr200_8960528bd01f_6d6a17121544_se1cs0fecef0 |
moq-rs | Rust, quinn / rustls | 031g0hlsr200_8960528bd01f_6d6a17121544_sr1csccfa1a9 |
moq and hysteria make use of datagrams and advertise it via the max_datagram_frame_size transport parameter, leading to unique JA4Scan fingerprints compared to a QUIC listener serving HTTP/3.
MASQUE
MASQUE allows proxying UDP inside HTTP/3 datagrams. VPN providers and others use it as a WireGuard obfuscation transport on port 443 with a publicly issued certificate to act as an ordinary server.
Deployment | JA4Scan-TLS | JA4Scan-QUIC |
masque-go v0.4.0 | - | 031g0hlss000_5973261134f6_6d6a1784efde_sr1csc7b63be |
Mullvad Hong Kong node | - | 031g0hlss000_5973261134f6_6d6a1784efde_sr1csc7b63be |
NordVPN af8 (nordwhisper_udp) | 03100h0s01s0_9d52559f55bc_d06dc4cede0d_sr00sc82d654 | 031g0hlss000_5973261134f6_6d6a1784efde_sr1csc7b63be |
CTYun (China Telecom Cloud) | 2000000s00s0_0d2c87c1f885_8eddc28b041c_s01s0015f721 | 031g0hlss000_5973261134f6_6d6a1784efde_sr1csc7b63be |
JA4Scan
JA4Scan was 100% human created with over a year of iteration and testing and is currently integrated into Security Scorecard's Driftnet.io, Hunt.io (Oct 1), MODAT and coming soon, if not already implemented, in Censys, Palo Alto Networks' Cortex Xpanse, Validin, and other EASM tools.
If you would like to do the scanning yourself, JA4Scan is free and provided under the FoxIO License, just like the rest of JA4+. However, we’ve decided not to make the source code available for general AI consumption at this time. As such, if you would like access to the JA4Scan tool, please just send your github username to info@foxio.io and we’ll gladly add you to the repo!
JA4Scan was created by Sébastien Féry and John Althouse (author of JARM).
