1
0
Fork
You've already forked ja3
0
No description
  • Go 99.8%
  • Shell 0.2%
Find a file
Phil 5aa380ebba
Merge pull request #17 from myles-keough/master
Trim any trailing value separators
2023年10月02日 10:26:00 +02:00
cmd add dump pkg support & bpf filter support 2022年07月27日 21:31:36 +08:00
.gitignore implemented support for ja3 server hashes 2020年03月03日 23:57:56 +01:00
AUTHORS first commit 2018年11月06日 10:15:16 +01:00
csv.go Allow opt-out from gopacket via build tag 2022年09月11日 19:26:08 +02:00
go.mod added version, timeout and promisc flags 2020年09月17日 13:25:56 +02:00
go.sum fix: handle different link types and add unit test for raw ip encapsulated dump file 2021年10月25日 20:33:39 +02:00
gopacket.go Allow opt-out from gopacket via build tag 2022年09月11日 19:26:08 +02:00
install.sh cleanup, benchmarks, tests and readme 2018年11月16日 23:14:33 +01:00
ja3.go Trim any trailing value separators 2023年08月30日 14:33:28 -05:00
ja3_output_testpcap.json implemented support for ja3 server hashes 2020年03月03日 23:57:56 +01:00
ja3_test.go add dump pkg support & bpf filter support 2022年07月27日 21:31:36 +08:00
ja3s.go Trim any trailing value separators 2023年08月30日 14:33:28 -05:00
ja3s_pcap.go Allow opt-out from gopacket via build tag 2022年09月11日 19:26:08 +02:00
ja3s_test.go fix: handle different link types and add unit test for raw ip encapsulated dump file 2021年10月25日 20:33:39 +02:00
ja3s_test.log implemented support for ja3 server hashes 2020年03月03日 23:57:56 +01:00
json.go Allow opt-out from gopacket via build tag 2022年09月11日 19:26:08 +02:00
LICENSE changed to BSDv3 license like the reference implementation, updated readme 2018年11月17日 12:31:03 +01:00
live.go Format imports 2022年09月11日 19:26:13 +02:00
README.md added version, timeout and promisc flags 2020年09月17日 13:25:56 +02:00
test.pcap debug mode, added 10MB example dump pcap for tests, fixed csv output 2018年11月19日 01:20:14 +01:00
test2.pcap fix: handle different link types and add unit test for raw ip encapsulated dump file 2021年10月25日 20:33:39 +02:00
TODO.md debug mode, added 10MB example dump pcap for tests, fixed csv output 2018年11月19日 01:20:14 +01:00
utils.go Format imports 2022年09月11日 19:26:13 +02:00

JA3

Go Report Card License Golang Linux macOS Windows GoDoc

JA3 is a technique developed by Salesforce, to fingerprint the TLS client and server hellos.

The official python implementation can be found here. More details can be found in their blog post: https://engineering.salesforce.com/open-sourcing-ja3-92c9e53c3c41

This package provides a pure golang implementation of both the client and server hash functions, with unit tests to ensure correct behavior and performance. This implementation uses dreadl0ck/gopacket for packet decoding.

The bradleyfalzon/tlsx fork used for parsing the TLS handshakes (dreadl0ck/tlsx ), has been extended to support extracting the server hello message, unit tests, benchmarks and a faster decoding interface for use with Ja3 fingerprinting.

Assembly of the ja3 bare was previously implemented with the golang 1.10 strings.Builder type, however using byte slices for this turned out to be faster and causes less allocations. (Thanks for the tips @lukechampine) Thanks to @guigzzz for his pull request on further reducing allocations!

Package Usage

This package exports the following API:

Live Capture from interface, outputs CSV with configurable separator or JSON:

funcReadInterface(ifacestring,outio.Writer,separatorstring,ja3sbool,asJSONbool,snaplenint,promiscbool,timeouttime.Duration){

Files:

funcReadFileJSON(filestring,outio.Writer,doJA3sbool)
funcReadFileCSV(filestring,outio.Writer,separatorstring,doJA3sbool)

Read file and only print Ja3s values, mimics the python implementation from salesforce:

funcReadFileJa3s(filestring,outio.Writer)

Using gopacket.Packet:

funcBarePacket(pgopacket.Packet)[]byte
funcBarePacketJa3s(pgopacket.Packet)[]byte
funcDigestPacket(pgopacket.Packet)[md5.Size]byte
funcDigestPacketJa3s(pgopacket.Packet)[md5.Size]byte
funcDigestHexPacket(pgopacket.Packet)string
funcDigestHexPacketJa3s(pgopacket.Packet)string

Using tlsx.ClientHello:

funcBare(hello*tlsx.ClientHello)[]byte
funcBareJa3s(hello*tlsx.ServerHello)[]byte
funcDigest(hello*tlsx.ClientHello)[md5.Size]byte
funcDigestJa3s(hello*tlsx.ServerHello)[md5.Size]byte
funcDigestHex(hello*tlsx.ClientHello)string
funcDigestHexJa3s(hello*tlsx.ServerHello)string

Commandline Tool

The commandline program mimics the python reference implementation by default. It reads the pcap file and prints the all extracted client hellos as JSON array to stdout. Printing output as CSV, tab separated values or with a custom separator is also supported.

$ goja3 -h
Usage of goja3:
 -csv
 	print as CSV
 -debug
 	toggle debug mode
 -iface string
 	specify network interface to read packets from
 -ja3s
 	dump ja3s only
 -json
 	print as JSON array (default true)
 -read string
 	read PCAP file
 -separator string
 	set a custom separator (default ",")
 -tsv
 	print as TAB separated values

Benchmark of the python reference implementation VS this one, on a 109 MB PCAP dumpfile (DEF CON 23 ICS Village.pcap). This dump file is interesting for comparison because it contains handshakes without extensions set, as well as TLS traffic over non standard ports.

Python implementation:

# https://github.com/salesforce/ja3
$ time ja3 -a DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
real	0m29.031s
user	0m28.803s
sys	0m0.141s

Go implementation:

# github.com/dreadl0ck/ja3
# disable server fingerprint generation to mimic the behavior of the reference implementation
$ time goja3 -ja3s=false -json -read DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
real	0m0.996s
user	0m2.245s
sys	0m0.166s

Output:

[
 ...
 {
 "destination_ip": "192.168.0.60",
 "destination_port": 443,
 "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
 "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
 "ja3s": "",
 "ja3s_digest": "",
 "source_ip": "192.168.0.112",
 "source_port": 59935,
 "timestamp": "1438978270.855224"
 },
 {
 "destination_ip": "192.168.0.30",
 "destination_port": 443,
 "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
 "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
 "ja3s": "",
 "ja3s_digest": "",
 "source_ip": "192.168.0.112",
 "source_port": 42455,
 "timestamp": "1438978270.855228"
 }
]

JA3 Details

JA3 gathers the decimal values of the bytes for the following fields: SSL Version, Accepted Ciphers, List of Extensions, Elliptic Curves, and Elliptic Curve Formats.

It then concatenates those values together in order, using a "," to delimit each field and a "-" to delimit each value in each field.

The field order is as follows:

SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats

Example:

769,47–53–5–10–49161–49162–49171–49172–50–56–19–4,0–10–11,23–24–25,0

If there are no SSL Extensions in the Client Hello, the fields are left empty.

Example:

769,4–5–10–9–100–98–3–6–19–18–99,,,

These strings are then MD5 hashed to produce an easily consumable and shareable 32 character fingerprint.

This is the JA3 SSL Client Fingerprint.

JA3 is a much more effective way to detect malicious activity over SSL than IP or domain based IOCs. Since JA3 detects the client application, it doesn’t matter if malware uses DGA (Domain Generation Algorithms), or different IPs for each C2 host, or even if the malware uses Twitter for C2, JA3 can detect the malware itself based on how it communicates rather than what it communicates to.

JA3 is also an excellent detection mechanism in locked-down environments where only a few specific applications are allowed to be installed. In these types of environments one could build a whitelist of expected applications and then alert on any other JA3 hits.

For more details on what you can see and do with JA3 and JA3S, please see this Shmoocon 2018 talk: https://youtu.be/oprPu7UIEuk?t=6m44s

JA3S Details

JA3S is JA3 for the Server side of the SSL/TLS communication and fingerprints how servers respond to particular clients.

JA3S uses the following field order:

SSLVersion,Cipher,SSLExtension

With JA3S it is possible to fingerprint the entire cryptographic negotiation between client and it's server by combining JA3 + JA3S. That is because servers will respond to different clients differently but will always respond to the same client the same.

For the Trickbot example:

JA3 = 6734f37431670b3ab4292b8f60f29984 ( Fingerprint of Trickbot )
JA3S = 623de93db17d313345d7ea481e7443cf ( Fingerprint of Command and Control Server Response )

For the Emotet example:

JA3 = 4d7a28d6f2263ed61de88ca66eb011e3 ( Fingerprint of Emotet )
JA3S = 80b3a14bccc8598a1f3bbe83e71f735f ( Fingerprint of Command and Control Server Response )

In these malware examples, the command and control server always responds to the malware client in exactly the same way, it does not deviate. So even though the traffic is encrypted and one may not know the command and control server's IPs or domains as they are constantly changing, we can still identify, with reasonable confidence, the malicious communication by fingerprinting the TLS negotiation between client and server. Again, please be aware that these are examples, not indicative of all versions ever, and are intended to illustrate what is possible.

Benchmarks & Tests

Run the benchmarks and the correctness test with:

$ go test -v -bench=.
=== RUN TestDigestHexCorrect
--- PASS: TestDigestHexCorrect (0.00s)
=== RUN TestDigestHexComparePcap
--- PASS: TestDigestHexComparePcap (0.10s)
=== RUN TestDigestHexJa3sCorrect
--- PASS: TestDigestHexJa3sCorrect (0.00s)
=== RUN TestDigestHexJa3sComparePcap
--- PASS: TestDigestHexJa3sComparePcap (0.10s)
goos: darwin
goarch: amd64
pkg: github.com/dreadl0ck/ja3
BenchmarkDigestHexPacket
BenchmarkDigestHexPacket-12 	 781578	 1499 ns/op	 352 B/op	 9 allocs/op
BenchmarkDigestPacket
BenchmarkDigestPacket-12 	 827286	 1406 ns/op	 288 B/op	 7 allocs/op
BenchmarkBarePacket
BenchmarkBarePacket-12 	 1000000	 1212 ns/op	 288 B/op	 7 allocs/op
BenchmarkDigestHex
BenchmarkDigestHex-12 	 895094	 1278 ns/op	 256 B/op	 3 allocs/op
BenchmarkDigest
BenchmarkDigest-12 	 996800	 1318 ns/op	 192 B/op	 1 allocs/op
BenchmarkBare
BenchmarkBare-12 	 1101476	 1188 ns/op	 192 B/op	 1 allocs/op
BenchmarkDigestHexPacketJa3s
BenchmarkDigestHexPacketJa3s-12 	 1654360	 619 ns/op	 120 B/op	 4 allocs/op
BenchmarkDigestPacketJa3s
BenchmarkDigestPacketJa3s-12 	 2328241	 513 ns/op	 56 B/op	 2 allocs/op
BenchmarkBarePacketJa3s
BenchmarkBarePacketJa3s-12 	 3294260	 387 ns/op	 56 B/op	 2 allocs/op
BenchmarkDigestHexJa3s
BenchmarkDigestHexJa3s-12 	 2476090	 464 ns/op	 112 B/op	 3 allocs/op
BenchmarkDigestJa3s
BenchmarkDigestJa3s-12 	 3321294	 357 ns/op	 48 B/op	 1 allocs/op
BenchmarkBareJa3s
BenchmarkBareJa3s-12 	 4964508	 238 ns/op	 48 B/op	 1 allocs/op
PASS
ok 	github.com/dreadl0ck/ja3	18.414s

Performance comparison with other implementations:

# https://github.com/open-ch/ja3
$ time ja3exporter -pcap DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
real	0m0.912s
user	0m1.979s
sys	0m0.142s
# github.com/dreadl0ck/ja3
# disable server fingerprint generation to mimic the behavior of the other implementations
$ time goja3 -ja3s=false -json -read DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
real	0m0.996s
user	0m2.245s
sys	0m0.166s
# https://github.com/salesforce/ja3
$ time ja3 -a DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
real	0m29.031s
user	0m28.803s
sys	0m0.141s

Number of records detected:

# https://github.com/open-ch/ja3
$ time ja3exporter -pcap DEF\ CON\ 23\ ICS\ Village.pcap | wc -l
138
# github.com/dreadl0ck/ja3
$ goja3 -ja3s=false -json -read DEF\ CON\ 23\ ICS\ Village.pcap | jq '. | length'
138
# https://github.com/salesforce/ja3
$ time ja3 -a DEF\ CON\ 23\ ICS\ Village.pcap | jq '. | length'
138
# https://github.com/salesforce/ja3
# without -a flag, only tls traffic over port 443 will be extracted
$ time ja3 DEF\ CON\ 23\ ICS\ Village.pcap | jq '. | length'
96

Notes

The ja3_output_testpcap.json file for output comparison in the unit tests was generated with the python reference implementation using:

ja3 -a test.pcap > ja3_output_testpcap.json