Impersonating JA3 Fingerprints

Matthew Rinaldi
CU Cyber
Published in
9 min readNov 23, 2019

Authors: Max Harley, Matthew Rinaldi

Link to project: ja3transport

Abstract

JA3 is a method for fingerprinting TLS clients using options in the TLS ClientHello packet like SSL version and available client extensions. At its core, this method of detecting malicious traffic is marginally better than the User-Agent header in HTTP since the client is in control of the ClientHello packet. Currently, there is no tooling available to easily craft ClientHello packets, so the JA3 hash is a great detection mechanism. A team of two members from CU Cyber have created a Go library that makes it easy to impersonate JA3 signatures.

What is JA3?

To understand JA3, you should first have a basic understanding of TLS. Browsers use TLS to make a secure connection to a server requested by the user. For example, when typing a username and password into the login field for Facebook.com, a browser will use TLS to ensure network appliances between the browser and Facebook’s servers can’t see the plaintext username and password. If TLS is not used, a hacker can break into said network appliance and steal sensitive user information without needing to decrypt the traffic.

What makes TLS secure are the cryptographic functions used between the server and user’s browser (which I will be calling the “client” from now on). Cryptographic functions that take plaintext and output cipher text are called ciphers. Since there are many ciphers available to use, the server and client must agree on what cipher to use when communicating. In TLS, the cipher is chosen by a negotiation between the client and server. The client makes the first request (ClientHello) with all the available ciphers (and extensions) that it supports. The server then responds (ServerHello) with a cipher that both the client and server support. Now that the server and client know how to communicate, they are able to pass encrypted data back and forth using the chosen cipher.

Figure 1: List of cipher suites available in macOS Safari

Figure 1 shows part of a ClientHello message to https://google.com coming from the macOS Safari browser. In this part of the message, a list of cipher suites that the client has available is shown.

Figure 2: Chosen cipher by the server

Figure 2 shows the ServerHello response to the previous ClientHello. Google has chosen TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 from the list of ciphers available in the ClientHello request.

In addition to ciphers, there are TLS extensions. Extensions allow clients and servers to communicate more effectively or with additional information about each other. TLS extensions won’t be covered in this blog post. More information can be found in the TLS RFC.

A group of researchers consisting of John Althouse, Jeff Atkinson, and Josh Atkins realized that TLS libraries communicate using the same five parameters from the ClientHello message each time. You can think of it like a better HTTP user agent. JA3 is a useful detection mechanism for the blue team since some malware and C2 agents have unique JA3 signatures. For example, the JA3 signature hash of Meterpreter on Windows is b386946a5a44d1ddcc843bc75336dfce.

The five ClientHello parameters JA3 uses are the TLS version, list of cipher suites, list of extensions, list of elliptic curves, and list of elliptic curve point formats. The parameters are separated by commas and each element in the list is separated by dashes in the order they appear. Below is an example of how a JA3 signature is generated given a single ClientHello packet. If you are interested in deeply understanding how JA3 generation works, we recommend downloading and opening Wireshark, navigating to a website using HTTPS, and finding the ClientHello message (use the search term “tls” to find it quicker).

Figure 3: TLS Version

The first parameter JA3 uses is the TLS version. Figure 3 shows the TLS version the client would like to communicate with. The JA3 format uses decimal, so the first parameter is the value 0x0303 in hexadecimal or 771 in decimal.

Figure 4: List of available ciphers

The second JA3 parameter is the list of available client ciphers. Again, JA3 uses decimal instead of hex. The list of ciphers separated by dashes is

4865–4866–4867–49196–49195–49188–49187–49162–49161–52393–49200–49199–49192–49191–49172–49171–52392–157–156–61–60–53–47–49160–49170–10
Figure 5: TLS Extensions

The third JA3 parameter is the list of available extensions. Each extension is denoted in the TLS packet by a number. IANA has a great list of TLS extensions here. Mapping the names to the values in the table, the list of available extensions in this request is:

65281–0–23–13–5–18–16–11–51–45–43–10–21
Figure 6: Elliptic curves

The fourth JA3 parameter is supported elliptic curves. This is denoted by the supported_groups extension. In decimal and separated by dashes, the fourth parameter is

29-24-23-25
Figure 7: Elliptic curve point formats

The fifth JA3 parameter is supported elliptic curve point formats. This is denoted by the ec_point_formats extension. In this request, there is only you format: the value 0.

Putting all these parameters together, you get the JA3 signature of

771,4865–4866–4867–49196–49195–49188–49187–49162–49161–52393–49200–49199–49192–49191–49172–49171–52392–157–156–61–60–53–47–49160–49170–10,65281–0–23–13–5–18–16–11–51–45–43–10–21,29–23–24–25,0

The website https://ja3er.com takes JA3 signatures and guesses what browser is being used. Using the JA3 signature above, the website returns

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36

The user agent string above correctly matches the browser used to make the request. In most cases, you will see the JA3 string hashed using MD5. Sharing the hash instead of the full JA3 string takes much less space and has a low chance of collision. The MD5 hash of the signature above results in 6fa3244afc6bb6f9fad207b6b52af26b.

In summary, the JA3 signature is found by combining five parameters from the ClientHello TLS packet. Browsers and TLS libraries use the same five values (in most scenarios), so detecting what kind of client is requesting an HTTPS website is as easy as looking up a JA3 signature in a database. Malicious software, like Meterpreter, have unique JA3 signatures, so it’s a great detection mechanism for the blue team.

Impersonating JA3

The issue with JA3 is that it is only marginally better than fingerprinting clients based on the user agent string. The user agent string has, until now, been much easier to change than the TLS ClientHello packet. The parameters of the JA3 signature are still controlled by the TLS client, so it cannot be used as a trusted source of information. In Jeff and John’s ShmooCon talk, they mention that JA3 is not a silver bullet [1]. Their proposed method of subverting JA3 detections is to use the operating system’s HTTPS client to bypass TLS client-specific JA3 signatures. We propose another way to subvert JA3 detections by crafting ClientHello packets that match other TLS clients like browsers.

To subvert JA3, you need to change the five JA3 parameters which can be modified in the ClientHelloSpec struct provided by Refraction Networking’s utls project. The package allows the user to construct and execute the ClientHello handshake. The first parameter, TLS version, can be modified with the TLSVersionMin and TLSVersionMax members. The second parameter, available cipher suites, can be changed by updating the CipherSuites member. The third parameter, TLS extensions, can be changed by updating the Extensions member. One issue with the Extensions parameter is that all arguments must follow the TLSExtension interface. This means that we are limited to advertising extensions that the library provides (unless somebody would like to implement more). The fourth and fifth parameters, elliptic curves and elliptic curve point formats, are part of TLS extensions SupportedCurvesExtension and SupportedPointsExtension, respectively.

Our Library

With this blog post we are releasing a Go library called JA3Transport. The library allows offensive Go tools to make HTTPS requests using a custom JA3 fingerprint. Even though there are limitations on what TLS extensions can be advertised, the library implements both http.Client and http.Transport which make it easy, yet powerful, to use for beginner and experienced Go developers.

Instead of creating a client based on a JA3 string, we made it possible to generate a JA3 signature matching a benign signature like a web browser. There are presets that allow the JA3 signature to match Chrome or Firefox, and potentially more. We are only limited to masking applications that use the same extensions that Go has available. For example, if Chrome uses an extension that Go does not support, we are unable to mask it. Masking cipher suites is trickier, because any cipher suite can be advertised, even if it is not implemented. A problem occurs if the server performing the handshake accepts a cipher suite that the client advertised, but does not actually support. As long as the server accepts a cipher suite that is actually supported, falsely advertising more cipher suites than available is a nonissue.

Figure 8: http.Transport object

Go’s net/http library has a struct called Transport. The transport struct is in charge of writing how packets are sent to the target server. Since JA3‘s signature is based on the ClientHello packet, we can make the TLS handshake and let Go do the rest. For those experienced with Go, we make the handshake in the underlying net.Conn and return the connection to theDialTLS Transport member. The Transport object is a parameter for the http.Client struct which most Go developers are familiar with. By generating aTransport struct, our library should work seamlessly with any existing Go project.

Figure 9: JA3Client struct

We also created a helper struct called JA3Client that acts like the http.Client struct. The example above shows that theJA3Client is much easier to use than Transport, but working with the raw transport provides much more flexibility. The JA3Client struct can be created with by aBrowser type. Browser will set both the JA3 string and user agent (provided you have not already supplied one by in the http.Request)

Detection

We believe it’s important to release a detection mechanism after (or preferably before) an open source red team tool is released. We also understand that it’s much easier to propose a detection mechanism in a blog post than it is for blue teams to implement it at scale.

The simplest detection improvement we could think of is to pair JA3 signatures with the process image producing the TLS ClientHello packet. If there is a client producing a JA3 signature that matches Firefox, but the process is not Firefox, there is likely something strange occurring.

Conclusion

JA3 is a great detection mechanism when attackers are not aware of the fingerprint they’re leaving behind. Once attackers learn they can easily change the JA3 signature for their tools, changing the JA3 signature will be as necessary as changing the user agent field in current offensive tooling. We recognize that organizations rely on JA3 signatures to find attackers, but it was only a matter of time before an easy-to-use library was created to manipulate the fingerprint. We believe that releasing this tool will push blue teams to create more robust detections than are currently available.

Our research group is not done with JA3. First, the JA3Transport library is far from perfect. We will continue development on the project for at least a semester. Next, we want to implement this technique in open source Go C2 agents like Merlin and Apfell’s Poseidon. Finally, we would like to research new ways of fingerprinting TLS traffic that would subvert the JA3Transport library in order to continue the cat-and-mouse game of TLS client detection.

We have thought of possible ways to strengthen JA3’s current implementation and determine if clients are spoofing their JA3 signatures. Since clients should not advertise extensions and ciphers they don’t support, you can confidently reduce the list of potential TLS clients. For example, the refraction-networking/utls library does not support extensions one through four. Therefore, if a JA3 signature contains an extension between one and four, one can be fairly confident that a Go client is not being used. Our team has not verified that this would be a useful way to determine which TLS client is being used, but it’s a promising idea that we plan to research more.

CU Cyber Lab

Clemson University’s cybersecurity club, CU Cyber, is experimenting with participating in the larger cybersecurity industry by working on research and developing open source software. The club has historically focused on teaching beginners and participating in undergraduate collegiate cybersecurity competitions. While we still believe that competitions are a valuable part of the club, we would also like to give experienced members the opportunity to participate in cutting-edge research and development.

If you have any questions or comments, please reach out to cyber[at]clemson.edu.

--

--