A high performance Go Implementation of the JA3 Client Fingerprinting Algorithm.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
cli Activating compatibility mode Dec 20, 2018
.travis.yml Adding README and preparation of Travis CI support Dec 19, 2018
LICENSE
README.md Polishing and minor correction of style in README Dec 21, 2018
doc.go Activating compatibility mode Dec 20, 2018
error.go
ja3.go Unify caching in getter functions Dec 20, 2018
ja3_test.go
parser.go Adding Copyright header to source files Dec 20, 2018

README.md

JA3 - High Performance Go Implementation GoDoc Go Report Card

"JA3 is a method for creating SSL/TLS client fingerprints that should be easy to produce on any platform and can be easily shared for threat intelligence." - John B. Althouse

The algorithm originates from Salesforce and the official Python and Bro implementations can be found here. For information about what JA3 is and how it works check out their repository or their blog post.

This package includes a go library with an implementation of the algorithm and a command line tool which allows reading packets from pcap and pcapng files as well as from an interface.

Our requirements on the Go Implementation of the JA3 library were efficiency, robustness and correctness. Unfortunately no public available implementation did fulfill all these requirements.

Usage

import "github.com/open-ch/ja3"

See the following example to get an idea of the exposed API. For more information consult the godoc.

j, err := ja3.ComputeJA3FromSegment(tcpPayload)
if err != nil {
    // If the packet is no Client Hello an error is thrown as soon as the parsing fails
    panic(err)
}

// Get the JA3 digest, string and SNI of the parsed Client Hello
ja3Hash := j.GetJA3Hash()
ja3String := j.GetJA3String()
sni := j.GetSNI()
fmt.Printf("JA3Hash: %v, JA3String: %v, SNI: %v\n", ja3Hash, ja3String, sni)

// Get the JA3 string as a byte array for more efficient handling
ja3String := j.GetJA3ByteString()
anyWriterClass.Write(ja3String)

To check out the CLI, try the following on your preferred shell.

[host:]# go build ja3exporter.go engine.go

[host:]# ./ja3exporter -pcap="/path/to/file"
{"destination_ip":"172.217.168.67","destination_port":443,"ja3":"771,49200-49196-49199-49195-49172-49162-49171-49161-159-158-57-51-157-156-53-47-10-255,0-11-10-35-13-5-15-13172,23-25-28-27-24-26-22-14-13-11-12-9-10,0-1-2","ja3_digest":"5e647d60a56d199388ae462b75b3cdad","source_ip":"213.156.236.180","source_port":34577,"sni":"www.google.ch","timestamp":1537516825571014000}

Attention: By default, the JA3Exporter only supports packets built up of an Ethernet - IPv4 or IPv6 - TCP Stack.

If the package structure does not comply with this, use the -c flag for compatibility mode. Beware that this will make the JA3Exporter significantly slower.

Tests and Benchmarks

As the TLS parser is custom built and highly optimized for the JA3 digest, a full coverage testing suite is put in place. Our Go implementation is more than an order of magnitude faster than the python implementation.

// JA3Exporter
time ja3exporter -pcap="/Users/enm/Documents/pcaps/DEF CON 23 ICS Village.pcap" > /dev/null
0.46s user
0.05s system
113% cpu
0.453 total

// Official Python Implmentation
time ja3 ~/Documents/pcaps/DEF\ CON\ 23\ ICS\ Village.pcap > /dev/null
23.10s user
0.40s system
98% cpu
23.874 total

// Dreadl0cks Go Implementation
time goja3 -read=/Users/enm/Documents/pcaps/DEF\ CON\ 23\ ICS\ Village.pcap > /dev/null
2.47s user
0.11s system
164% cpu
1.565 total

The pcap file used for the above tests can be found here.

Benchmarks of the Library functions on initial call:

goos: darwin
goarch: amd64
BenchmarkComputeJA3FromSegment-4   	 3000000	       367 ns/op	     304 B/op	       6 allocs/op
BenchmarkGetJA3ByteString-4        	10000000	       216 ns/op	     128 B/op	       1 allocs/op
BenchmarkGetJA3String-4            	 5000000	       253 ns/op	     192 B/op	       2 allocs/op
BenchmarkGetJA3Hash-4              	 3000000	       557 ns/op	     192 B/op	       3 allocs/op
BenchmarkGetSNI-4                  	300000000	         5.60 ns/op	       0 B/op	       0 allocs/op

The getter functions are setup to cache their results so any further access to these values perform as follows:

goos: darwin
goarch: amd64
BenchmarkGetJA3ByteString-4        	1000000000	         2.65 ns/op	       0 B/op	       0 allocs/op
BenchmarkGetJA3String-4            	50000000	        39.3 ns/op	      64 B/op	       1 allocs/op
BenchmarkGetJA3Hash-4              	1000000000	         2.40 ns/op	       0 B/op	       0 allocs/op
BenchmarkGetSNI-4                  	300000000	         5.57 ns/op	       0 B/op	       0 allocs/op