Dissecting JA4H for improved Sliver C2 detections
Background
On November 18, 2024, Palo Alto Networks announced the discovery of two critical vulnerabilities, CVE-2024-0012 and CVE-2024-9474, in the operating system that powers their firewall devices. The following day, watchTowr published a report detailing how these vulnerabilities could be chained together to achieve remote code execution.
Shortly after the watchTowr report became public, Arctic Wolf Labs detected a series of intrusions targeting Palo Alto Networks devices. Their findings were reported on November 22 in a blogpost titled “Arctic Wolf Observes Threat Campaign Targeting Palo Alto Networks Firewall Devices”.
Yesterday, FoxIO’s John Althouse reported that several of the IoCs reported by Arctic Wolf can be replaced by the following JA4H fingerprint: po11cn050000_bb52516416a2_eb49a3237520_*
. Based on this fingerprint, we were able to confirm several victims of the activity reported by Arctic Wolf, supporting its validity and the strength of the JA4+ suite.
This blogpost dissects the fingerprint presented by John Althouse and presents two additional JA4H fingerprints observed in the activity cluster described by Arctic Wolf. It also concludes that an even more general fingerprint, po11cn050000_bb52516416a2_*
, can uncover the same activity with an acceptable false positive rate.
Understanding JA4H
On November 26 2024, John Althouse made the following Tweet sharing a JA4H fingerprint with the value of po11cn050000_bb52516416a2_eb49a3237520_*
:
In the family of JA4+ fingerprints, JA4H is responsible for fingerprinting HTTP - specifically the client that sends the request. It examines the request method, header names, cookies, cookie values, and other variables in each HTTP request to create a fingerprint that is both human- and machine-readable.
JA4H consists of four parts a, b, c, and d:
JA4H_a
focuses on the high-level stuff such as HTTP method, HTTP version, whether there is a cookie present or not, whether there is a referrer header, and the number of headers.JAH4_b
focuses more specifically on the headers observed in the request, excluding Cookie and Referrer. It is a truncated SHa256 value of the headers in the order they appear.JA4H_c
is a fingerprint of the cookie fields and will be different for each website visited but will be the same for that website or application.JA4H_d
is the most elaborate as it encompasses both cookie fields and their values.
This structure makes the JA4H fingerprint highly dynamic from a detection and threat hunting perspective. The fact that it becomes increasingly specific as one moves from part _a to _d, combined with the fact that important request artefacts are humanly readable in section _a, makes it flexible and easy to modify on the fly.
Below is a breakdown of the JA4H from Figure 1:
This dissection demonstrates that just by reading the fingerprint and knowing how it is generated, we can learn a lot about the underlying request and begin to experiment with custom detections. For example, we could look for similar requests with a different number of headers (po11cn*0000_*_eb49a3237520_*
), or we could look for similar requests without any cookies set (po11nn050000_bb52516416a2_*
).
The three last parts of the JA4H fingerprint, JA4H _b, _c, and _d, are harder to reproduce because they are not humanly readable. As with all other cryptographic hashes, we can’t just modify certain values or use wildcard characters to include or exclude elements of our choice.
To reproduce the JA4H_b and JA4H_c parts of the fingerprint shared by Althouse, bb52516416a2
and eb49a3237520
, we must know which headers the malware sets. In this case, the malware reaches out to its C2 over HTTP, so we don’t have to do any hard lifting in terms of reverse engineering to uncover the headers:
The figure above shows a request sent by the malware (red) and the response it received from the C2 server (blue). From this, we can confirm that the malware indeed sets 5 headers (not counting Cookies and Referer): Host
, User-Agent
, Content-Length
, Upgrade-Insecure-Requests
, and Accept-Encoding
, As indicated by the first part of the JA4H hash, po11cn050000
. With this CyberChef recipe, we can now reproduce the JA4H_b:
We can also reproduce the JA4H_c which is simply the truncated SHA256 of the only cookie the malware sets, “SSID”
:
import hashlib
hashlib.sha256("SSID".encode()).hexdigest()[:12]
# eb49a3237520
We can also use the recipe under Figure 3 to “fuzz” other signatures of the malware if we suspect it might use different headers or order of headers. The more we know about the malware and how it behaves on the network, the better we can tweak the JA4H fingerprint to make stronger and more granular detections.
In this case, the actor used the popular open-source C2 framework, Sliver. Open-source frameworks like this offer unique detection opportunities - we don’t need to have a sample, and we don’t have to be seasoned reverse engineers. Even though many such frameworks offer detection obfuscation “out of the box”, bad actors often do not change the default configurations. For example, Sliver’s HTTP C2 server defaults to using the following response headers if nothing is modified:
serverHeaders := []*clientpb.HTTPC2Header{
{
Method: "GET",
Name: "Cache-Control",
Value: "no-store, no-cache, must-revalidate",
Probability: 100,
},
}
Notice anything familiar with this code snippet to Figure 3 from before? These are the exact Cache-Control
header values we observed from the sample analyzed by Arctic Fox. This, in itself, is a detection opportunity.
Pivoting to uncover additional C2s
Webscout draws on a comprehensive set of sources to gather the metadata we use to enrich and contextualize IP addresses at scale, one of these being the JA4+ fingerprint suite (https://blog.webscout.io/introducing-ja4-support-in-webscout-elevating-threat-detection-with-advanced-network-fingerprinting/).
We decided to play around with different variations of the JA4H fingerprint in our newly implemented collection and ended up uncovering several victims and other C2s in the same activity cluster that was reported by Arctic Wolf.
By extending the wildcard to discard the JA4H_c part of the originally reported fingerprint, we uncovered two additional C2s active around the same time as WatchTowr demonstrated how the two Palo Alto CVEs could be chained together to achieve RCE :
172.232.195[.]234
194.182.164[.]149
Both of these are obvious Sliver C2s:
- They expose TCP port 31337
- The SSL Certificate Issuer value is
CN=operators
- The SSL Certificate Subject value is
CN=multiplayer
We observe the following two JA4H hashes in connections to these C2s:
po11cn050000_bb52516416a2_e1eae9e373ba_913d7ea84b88
po11cn050000_bb52516416a2_a9f2370d1a00_3e59a4bec10d
By assuming that the implant only sets a single cookie by default, as observed in the sample from Figure 3, we were quickly able to bruteforce the cookie names that result in the two new JA4H_c fingerprints that were not previously observed or reported:
cookies = [] # long ahh list of common cookie names
for cookie in cookies:
hash = hashlib.sha256(cookie.encode()).hexdigest()[:12] # JA4H_c
if hash == "e1eae9e373ba" or hash == "a9f2370d1a00":
print(f"{hash}:{cookie}")
> e1eae9e373ba:refreshToken
> a9f2370d1a00:csrf-state
Both of these are in the pool of cookie names used by Slivers default HTTP C2 config as can be seen in the source code here.
The ability to pivot from a single JA4H and identify several other malicious servers with minimal knowledge of the underlying malware highlights a key strength of the JA4+ suite: its capability to detect malicious "fingerprint neighbors" through subtle variations, cookies, headers, and other HTTP request artefacts.
Reflections
Actors still use default C2 configurations, leaving themselves open to detection. Malware developers go to great lengths to obscure their frameworks, yet sloppy actors offer detection opportunities by not customizing these settings. For example, the malware studied in this blog post, Sliver, can actively manipulate its JARM fingerprint by randomizing TLS cipher suites:
// Randomize the cipher suites
allCipherSuites := []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA, //uint16 = 0x0005 1
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, //uint16 = 0x000a 2
tls.TLS_RSA_WITH_AES_128_CBC_SHA, //uint16 = 0x002f 3
tls.TLS_RSA_WITH_AES_256_CBC_SHA, //uint16 = 0x0035 4
tls.TLS_RSA_WITH_AES_128_CBC_SHA256, //uint16 = 0x003c 5
tls.TLS_RSA_WITH_AES_128_GCM_SHA256, //uint16 = 0x009c 6
tls.TLS_RSA_WITH_AES_256_GCM_SHA384, //uint16 = 0x009d 7
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, //uint16 = 0xc007 8
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //uint16 = 0xc009 9
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, //uint16 = 0xc00a 10
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, //uint16 = 0xc011 11
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, //uint16 = 0xc012 12
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, //uint16 = 0xc013 13
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, //uint16 = 0xc014 14
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, //uint16 = 0xc023 15
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, //uint16 = 0xc027 16
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, //uint16 = 0xc02f 17
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, //uint16 = 0xc02b 18
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, //uint16 = 0xc030 19
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, //uint16 = 0xc02c 20
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, //uint16 = 0xcca8 21
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, //uint16 = 0xcca9 22
}
// CipherSuites ignores the order of the ciphers, this random shuffle
// is truncated resulting in a random selection from all ciphers
insecureRand.Shuffle(len(allCipherSuites), func(i, j int) {
allCipherSuites[i], allCipherSuites[j] = allCipherSuites[j], allCipherSuites[i]
})
nCiphers := insecureRand.Intn(len(allCipherSuites)-8) + 8
tlsConfig.CipherSuites = allCipherSuites[:nCiphers]
However, these anti-detection features are worthless if operators don’t utilize them. In terms of detectability and stealth, the chain is only as strong as its weakest link—in this case, the adversary.
Detection engineering is a cat-and-mouse game. Always has been, always will be. Methods such as the JA4+ suite give power back to the blue team by being intuitive to understand and manipulate, offering a high degree of flexibility to craft robust signatures. The more advanced the adversaries and their tools become, the more important it is to chain together individually weak pivot points into strong combined fingerprints. This is exactly where the JA4+ suite excels.
Another point to note is the continued use of unencrypted traffic in malware frameworks. How do threat actors get away with this? Because most organizations only log NetFlow data. NetFlow data contains high-level information about IP addresses, ports, session lengths, and byte sizes but rarely any layer-7 traffic details. This renders detection techniques such as the JA4+ suite useless. As long as organizations don’t inspect the actual contents of packets, unencrypted malicious traffic will continue to slip by unnoticed. This lack of packet-level visibility allows adversaries to exploit clear-text communication channels with minimal risk of detection, giving them an advantage they shouldn't have.
Conclusion
The use of JA4H fingerprints has proven to be an effective method for detecting and analyzing malicious activity, especially in the context of the recent exploitation of Palo Alto Networks firewall vulnerabilities. By dissecting and understanding the fingerprint shared by John Althouse, po11cn050000_bb52516416a2_eb49a3237520_*
, we were able to uncover additional command and control servers and validate the threat activity reported by Arctic Wolf.
The JA4+ suite enables researchers and analysts to pivot from known indicators and discover related malicious activities. This article has demonstrated how to find malicious JA4H "fingerprint neighbors" by making subtle changes in existing fingerprints based on known malware behavior.
IoCs
Sliver C2 IPs:
172.232.195[.]234
194.182.164[.]149
JA4H fingerprints
po11cn050000_bb52516416a2_e1eae9e373ba_913d7ea84b88
po11cn050000_bb52516416a2_a9f2370d1a00_3e59a4bec10d