ja3

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2019 License: BSD-3-Clause Imports: 4 Imported by: 0

README

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

Documentation

Overview

Package ja3 provides JA3 Client Fingerprinting for the Go language by looking at the TLS Client Hello packets.

Basic Usage ja3 takes in TCP payload data as a []byte and computes the corresponding JA3 string and digest.

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)

Index

Constants

View Source
const (
	LengthErr        string = "length check %v failed"
	ContentTypeErr   string = "content type not matching"
	VersionErr       string = "version check %v failed"
	HandshakeTypeErr string = "handshake type not matching"
	SNITypeErr       string = "SNI type not supported"
)

Error types

Variables

This section is empty.

Functions

This section is empty.

Types

type JA3

type JA3 struct {
	// contains filtered or unexported fields
}

JA3 stores the parsed fields from the Client Hello. To access the values use the respective getter methods.

func ComputeJA3FromHandshake added in v1.0.1

func ComputeJA3FromHandshake(handshake []byte) (*JA3, error)

ComputeJA3FromHandshake parses the handshake and returns the populated JA3 object or the encountered parsing error. Note: usually you'll want to use ComputeJA3FromSegment unless you're working directly with the raw data that makes up a TLS Handshake buffer.

func ComputeJA3FromSegment

func ComputeJA3FromSegment(payload []byte) (*JA3, error)

ComputeJA3FromSegment parses the segment and returns the populated JA3 object or the encountered parsing error.

func (*JA3) GetJA3ByteString

func (j *JA3) GetJA3ByteString() []byte

GetJA3ByteString returns the JA3 string as a byte slice for more efficient handling. This function uses caching, so repeated calls to this function on the same JA3 object will not trigger any new calculations.

func (*JA3) GetJA3Hash

func (j *JA3) GetJA3Hash() string

GetJA3Hash returns the MD5 Digest of the JA3 string in hexadecimal representation. This function uses caching, so repeated calls to this function on the same JA3 object will not trigger any new calculations.

func (*JA3) GetJA3String

func (j *JA3) GetJA3String() string

GetJA3String returns the JA3 string as a string. This function uses caching, so repeated calls to this function on the same JA3 object will not trigger any new calculations.

func (*JA3) GetSNI

func (j *JA3) GetSNI() string

GetSNI returns the set SNI in the Client Hello or an empty string if no SNI extension is found. This function uses caching, so repeated calls to this function on the same JA3 object will not trigger any new calculations.

type ParseError

type ParseError struct {
	// contains filtered or unexported fields
}

ParseError can be encountered while parsing a segment

func (*ParseError) Error

func (e *ParseError) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL