Repository: dreadl0ck/ja3
Branch: master
Commit: ff953f11e259
Files: 23
Total size: 127.9 KB
Directory structure:
gitextract_5b0w5x7_/
├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── TODO.md
├── cmd/
│ └── main.go
├── csv.go
├── go.mod
├── go.sum
├── gopacket.go
├── install.sh
├── ja3.go
├── ja3_output_testpcap.json
├── ja3_test.go
├── ja3s.go
├── ja3s_pcap.go
├── ja3s_test.go
├── ja3s_test.log
├── json.go
├── live.go
├── test.pcap
├── test2.pcap
└── utils.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea/
================================================
FILE: AUTHORS
================================================
Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
================================================
FILE: LICENSE
================================================
Copyright (c) 2017, Salesforce.com, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# JA3
[](https://goreportcard.com/report/github.com/dreadl0ck/ja3)
[](https://raw.githubusercontent.com/dreadl0ck/ja3/master/docs/LICENSE)
[](https://golang.org)



[](https://godoc.org/github.com/dreadl0ck/ja3)
JA3 is a technique developed by Salesforce, to fingerprint the TLS client and server hellos.
The official python implementation can be found [here](https://github.com/salesforce/ja3).
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 [gopacket/gopacket](https://github.com/gopacket/gopacket) for packet decoding.
If you don't need gopacket in your project and just want to use the logic to generate the Ja3 based on the input data, you can disable compiling with gopacket:
```
go build -tags ja3_disable_gopacket
```
The **[bradleyfalzon/tlsx](https://github.com/bradleyfalzon/tlsx)** fork used for parsing the TLS handshakes (**[dreadl0ck/tlsx](https://github.com/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:
```go
func ReadInterface(iface string, out io.Writer, separator string, ja3s bool, asJSON bool, snaplen int, promisc bool, timeout time.Duration) {
```
Files:
```go
func ReadFileJSON(file string, out io.Writer, doJA3s bool)
```
```go
func ReadFileCSV(file string, out io.Writer, separator string, doJA3s bool)
```
Read file and only print Ja3s values, mimics the python implementation from salesforce:
```go
func ReadFileJa3s(file string, out io.Writer)
```
Using gopacket.Packet:
```go
func BarePacket(p gopacket.Packet) []byte
```
```go
func BarePacketJa3s(p gopacket.Packet) []byte
```
```go
func DigestPacket(p gopacket.Packet) [md5.Size]byte
```
```go
func DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte
```
```go
func DigestHexPacket(p gopacket.Packet) string
```
```go
func DigestHexPacketJa3s(p gopacket.Packet) string
```
Using tlsx.ClientHello:
```go
func Bare(hello *tlsx.ClientHello) []byte
```
```go
func BareJa3s(hello *tlsx.ServerHello) []byte
```
```go
func Digest(hello *tlsx.ClientHello) [md5.Size]byte
```
```go
func DigestJa3s(hello *tlsx.ServerHello) [md5.Size]byte
```
```go
func DigestHex(hello *tlsx.ClientHello) string
```
```go
func DigestHexJa3s(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
================================================
FILE: TODO.md
================================================
# TODO
- compare number of records to output of python implementation
- 0 allocations for Bare()
================================================
FILE: cmd/main.go
================================================
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main
import (
"flag"
"fmt"
"github.com/dreadl0ck/ja3"
"github.com/gopacket/gopacket/pcap"
"os"
)
var (
flagJSON = flag.Bool("json", true, "print as JSON array")
flagCSV = flag.Bool("csv", false, "print as CSV")
flagTSV = flag.Bool("tsv", false, "print as TAB separated values")
flagSeparator = flag.String("separator", ",", "set a custom separator")
flagInput = flag.String("read", "", "read PCAP file")
flagDebug = flag.Bool("debug", false, "toggle debug mode")
flagInterface = flag.String("iface", "", "specify network interface to read packets from")
flagJa3S = flag.Bool("ja3s", true, "include ja3 server hashes (ja3s)")
flagOnlyJa3S = flag.Bool("ja3s-only", false, "dump ja3s only")
flagSnaplen = flag.Int("snaplen", 1514, "default snap length for ethernet frames")
flagPromisc = flag.Bool("promisc", true, "capture in promiscuous mode (requires root)")
// https://godoc.org/github.com/gopacket/gopacket/pcap#hdr-PCAP_Timeouts
flagTimeout = flag.Duration("timeout", pcap.BlockForever, "timeout for collecting packet batches")
flagVersion = flag.Bool("version", false, "display version and exit")
)
const version = "v1.0.2"
func main() {
flag.Parse()
if *flagVersion {
fmt.Println(version)
os.Exit(0)
}
ja3.Debug = *flagDebug
if *flagInterface != "" {
ja3.ReadInterface(*flagInterface, os.Stdout, *flagSeparator, *flagJa3S, *flagJSON, *flagSnaplen, *flagPromisc, *flagTimeout)
return
}
if *flagInput == "" {
fmt.Println("use the -read flag to supply an input file.")
os.Exit(1)
}
if *flagOnlyJa3S {
ja3.ReadFileJa3s(*flagInput, os.Stdout)
return
}
if *flagTSV {
ja3.ReadFileCSV(*flagInput, os.Stdout, "\t", *flagJa3S)
return
}
if *flagCSV {
ja3.ReadFileCSV(*flagInput, os.Stdout, *flagSeparator, *flagJa3S)
return
}
if *flagJSON {
ja3.ReadFileJSON(*flagInput, os.Stdout, *flagJa3S)
}
}
================================================
FILE: csv.go
================================================
//go:build !ja3_disable_gopacket
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3
import (
"fmt"
"io"
"strings"
"github.com/gopacket/gopacket"
)
// ReadFileCSV reads the PCAP file at the given path
// and prints out all packets containing JA3 digests to the supplied io.Writer
func ReadFileCSV(file string, out io.Writer, separator string, doJA3s bool) {
r, f, link, err := openPcap(file)
if err != nil {
panic(err)
}
defer f.Close()
columns := []string{"timestamp", "source_ip", "source_port", "destination_ip", "destination_port", "ja3_digest", "ja3s_digest"}
_, err = out.Write([]byte(strings.Join(columns, separator) + "\n"))
if err != nil {
panic(err)
}
count := 0
for {
// read packet data
data, ci, err := r.ReadPacketData()
if err == io.EOF {
if Debug {
fmt.Println(count, "fingerprints.")
}
return
} else if err != nil {
panic(err)
}
var (
// create gopacket
p = gopacket.NewPacket(data, link, gopacket.Lazy)
// get JA3 if possible
digest = DigestHexPacket(p)
isServer bool
)
if doJA3s && digest == "" {
digest = DigestHexPacketJa3s(p)
isServer = true
}
// check if we got a result
if digest != "" {
count++
var (
b strings.Builder
nl = p.NetworkLayer()
tl = p.TransportLayer()
)
// got an a digest but no transport or network layer
if tl == nil || nl == nil {
if Debug {
fmt.Println("got a nil layer: ", nl, tl, p.Dump(), digest)
}
continue
}
b.WriteString(timeToString(ci.Timestamp))
b.WriteString(separator)
b.WriteString(nl.NetworkFlow().Src().String())
b.WriteString(separator)
b.WriteString(tl.TransportFlow().Src().String())
b.WriteString(separator)
b.WriteString(nl.NetworkFlow().Dst().String())
b.WriteString(separator)
b.WriteString(tl.TransportFlow().Dst().String())
b.WriteString(separator)
if isServer {
b.WriteString("")
b.WriteString(separator)
b.WriteString(digest)
} else { // client
b.WriteString(digest)
b.WriteString(separator)
b.WriteString("")
}
b.WriteString("\n")
_, err := out.Write([]byte(b.String()))
if err != nil {
panic(err)
}
}
}
}
================================================
FILE: go.mod
================================================
module github.com/dreadl0ck/ja3
require (
github.com/dreadl0ck/tlsx v1.1.1
github.com/gopacket/gopacket v1.4.0
)
require (
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/net v0.46.0 // indirect
golang.org/x/sys v0.37.0 // indirect
)
go 1.24.0
================================================
FILE: go.sum
================================================
github.com/dreadl0ck/tlsx v1.1.1 h1:fclhQwhO2HmRm568JFBEX0G6S7hFGBsN/JBFh9RqgVU=
github.com/dreadl0ck/tlsx v1.1.1/go.mod h1:hqEz1A1Nt5lnFZh8nTE1iqkipCZGX7TWe+xXBWWPyo0=
github.com/gopacket/gopacket v1.4.0 h1:cr1OlFpzksCkZHNO0eLjaSSOrMQnpPXg0j6qHIY3y2U=
github.com/gopacket/gopacket v1.4.0/go.mod h1:EpvsxINeehp5qj4YMKMLf2/dekdhKn2IIAO/ZOifS7o=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
================================================
FILE: gopacket.go
================================================
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3
import (
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/dreadl0ck/tlsx"
)
// DigestPacket returns the Ja3 digest
// for a packet carrying a TLS Client Hello
// or an empty byte slice
func DigestPacket(p gopacket.Packet) [md5.Size]byte {
return md5.Sum(BarePacket(p))
}
// DigestPacket returns the Ja3s digest
// for a packet carrying a TLS Server Hello
// or an empty byte slice
func DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte {
return md5.Sum(BarePacketJa3s(p))
}
// DigestHexPacket returns the hex string for the packet
// for a packet carrying a TLS Client Hello
func DigestHexPacket(p gopacket.Packet) string {
bare := BarePacket(p)
if len(bare) == 0 {
return ""
}
sum := md5.Sum(bare)
return hex.EncodeToString(sum[:])
}
// DigestHexPacket returns the hex string for the packet
// for a packet carrying a TLS Client Hello
func DigestHexPacketJa3s(p gopacket.Packet) string {
bare := BarePacketJa3s(p)
if len(bare) == 0 {
return ""
}
sum := md5.Sum(bare)
return hex.EncodeToString(sum[:])
}
// BarePacket returns the Ja3 digest if the supplied packet contains a TLS client hello
// otherwise returns an empty string
func BarePacket(p gopacket.Packet) []byte {
if tl := p.TransportLayer(); tl != nil {
if tcp, ok := tl.(*layers.TCP); ok {
if tcp.SYN {
// Connection setup
} else if tcp.FIN {
// Connection teardown
} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
// Acknowledgement packet
} else if tcp.RST {
// Unexpected packet
} else {
// data packet
var (
hello = tlsx.ClientHelloBasic{}
err = hello.Unmarshal(tcp.LayerPayload())
)
if err != nil {
if Debug {
fmt.Println(err)
//fmt.Println(p.Dump())
}
return []byte{}
}
// return JA3 bare
return Bare(&hello)
}
}
}
return []byte{}
}
// BarePacket returns the Ja3 digest if the supplied packet contains a TLS client hello
// otherwise returns an empty string
func BarePacketJa3s(p gopacket.Packet) []byte {
if tl := p.TransportLayer(); tl != nil {
if tcp, ok := tl.(*layers.TCP); ok {
if tcp.SYN {
// Connection setup
} else if tcp.FIN {
// Connection teardown
} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
// Acknowledgement packet
} else if tcp.RST {
// Unexpected packet
} else {
// data packet
var (
hello = tlsx.ServerHelloBasic{}
err = hello.Unmarshal(tcp.LayerPayload())
)
if err != nil {
if Debug {
fmt.Println(err, p.NetworkLayer().NetworkFlow(), p.TransportLayer().TransportFlow())
//fmt.Println(p.Dump())
}
return []byte{}
}
// return JA3 bare
return BareJa3s(&hello)
}
}
}
return []byte{}
}
================================================
FILE: install.sh
================================================
#!/bin/bash
go build -o $(go env GOPATH)/bin/goja3 -i github.com/dreadl0ck/ja3/cmd
================================================
FILE: ja3.go
================================================
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3
import (
"bytes"
"crypto/md5"
"encoding/hex"
"strconv"
"github.com/dreadl0ck/tlsx"
)
var (
// Debug indicates whether we run in debug mode.
Debug = false
sepValueByte = byte(45)
sepFieldByte = byte(44)
// Ciphers, extensions and elliptic curves, should be filtered so GREASE values are not added to the ja3 digest
// GREASE RFC: https://tools.ietf.org/html/draft-davidben-tls-grease-01
greaseValues = map[uint16]bool{
0x0a0a: true, 0x1a1a: true,
0x2a2a: true, 0x3a3a: true,
0x4a4a: true, 0x5a5a: true,
0x6a6a: true, 0x7a7a: true,
0x8a8a: true, 0x9a9a: true,
0xaaaa: true, 0xbaba: true,
0xcaca: true, 0xdada: true,
0xeaea: true, 0xfafa: true,
}
)
// BareToDigestHex converts a bare []byte to a hex string.
func BareToDigestHex(bare []byte) string {
sum := md5.Sum(bare)
return hex.EncodeToString(sum[:])
}
// Digest returns only the digest md5.
func Digest(hello *tlsx.ClientHelloBasic) [md5.Size]byte {
return md5.Sum(Bare(hello))
}
// DigestHex produce md5 hash from bare string.
func DigestHex(hello *tlsx.ClientHelloBasic) string {
return BareToDigestHex(Bare(hello))
}
// Bare returns the JA3 bare string for a given tlsx.ClientHelloBasic instance
// JA3 is a technique developed by Salesforce, to fingerprint TLS Client Hellos.
// the official python implementation can be found here: https://github.com/salesforce/ja3
// 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 returned by this function.
func Bare(hello *tlsx.ClientHelloBasic) []byte {
// TODO: refactor into struct with inbuilt buffer to reduce allocations to ~ zero
// i.e. only realloc if previously allocated buffer is too small for current packet
var (
maxPossibleBufferLength = 5 + 1 + // Version = uint16 => maximum = 65536 = 5chars + 1 field sep
(5+1)*len(hello.CipherSuites) + // CipherSuite = uint16 => maximum = 65536 = 5chars
(5+1)*len(hello.AllExtensions) + // uint16 = 2B => maximum = 65536 = 5chars
(5+1)*len(hello.SupportedGroups) + // uint16 = 2B => maximum = 65536 = 5chars
(3+1)*len(hello.SupportedPoints) // uint8 = 1B => maximum = 256 = 3chars
buffer = make([]byte, 0, maxPossibleBufferLength)
)
buffer = strconv.AppendInt(buffer, int64(hello.HandshakeVersion), 10)
buffer = append(buffer, sepFieldByte)
/*
* Cipher Suites
*/
// collect cipher suites
lastElem := len(hello.CipherSuites) - 1
if len(hello.CipherSuites) > 1 {
for _, e := range hello.CipherSuites[:lastElem] {
// filter GREASE values
if !greaseValues[uint16(e)] {
buffer = strconv.AppendInt(buffer, int64(e), 10)
buffer = append(buffer, sepValueByte)
}
}
}
// append last element if cipher suites are not empty
if lastElem != -1 {
// filter GREASE values
if !greaseValues[uint16(hello.CipherSuites[lastElem])] {
buffer = strconv.AppendInt(buffer, int64(hello.CipherSuites[lastElem]), 10)
}
}
buffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})
buffer = append(buffer, sepFieldByte)
/*
* Extensions
*/
// collect extensions
lastElem = len(hello.AllExtensions) - 1
if len(hello.AllExtensions) > 1 {
for _, e := range hello.AllExtensions[:lastElem] {
// filter GREASE values
if !greaseValues[uint16(e)] {
buffer = strconv.AppendInt(buffer, int64(e), 10)
buffer = append(buffer, sepValueByte)
}
}
}
// append last element if extensions are not empty
if lastElem != -1 {
// filter GREASE values
if !greaseValues[uint16(hello.AllExtensions[lastElem])] {
buffer = strconv.AppendInt(buffer, int64(hello.AllExtensions[lastElem]), 10)
}
}
buffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})
buffer = append(buffer, sepFieldByte)
/*
* Supported Groups
*/
// collect supported groups
lastElem = len(hello.SupportedGroups) - 1
if len(hello.SupportedGroups) > 1 {
for _, e := range hello.SupportedGroups[:lastElem] {
// filter GREASE values
if !greaseValues[uint16(e)] {
buffer = strconv.AppendInt(buffer, int64(e), 10)
buffer = append(buffer, sepValueByte)
}
}
}
// append last element if supported groups are not empty
if lastElem != -1 {
// filter GREASE values
if !greaseValues[uint16(hello.SupportedGroups[lastElem])] {
buffer = strconv.AppendInt(buffer, int64(hello.SupportedGroups[lastElem]), 10)
}
}
buffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})
buffer = append(buffer, sepFieldByte)
/*
* Supported Points
*/
// collect supported points
lastElem = len(hello.SupportedPoints) - 1
if len(hello.SupportedPoints) > 1 {
for _, e := range hello.SupportedPoints[:lastElem] {
buffer = strconv.AppendInt(buffer, int64(e), 10)
buffer = append(buffer, sepValueByte)
}
}
// append last element if supported points are not empty
if lastElem != -1 {
buffer = strconv.AppendInt(buffer, int64(hello.SupportedPoints[lastElem]), 10)
}
return buffer
}
================================================
FILE: ja3_output_testpcap.json
================================================
[
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "768,22-19-10-102-5-4-101-100-99-98-97-96-21-18-9-20-17-8-6-3,,,",
"ja3_digest": "ee0799c323d74129b75b633dcfd41593",
"source_ip": "192.168.0.104",
"source_port": 47851,
"timestamp": 1438974952.485206
},
{
"destination_ip": "192.168.0.20",
"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",
"source_ip": "192.168.0.104",
"source_port": 47852,
"timestamp": 1438974952.499013
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47436,
"timestamp": 1438975304.430254
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47437,
"timestamp": 1438975310.453851
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47438,
"timestamp": 1438975310.80441
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47439,
"timestamp": 1438975311.016676
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47440,
"timestamp": 1438975312.045158
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47441,
"timestamp": 1438975312.182204
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 47442,
"timestamp": 1438975312.918489
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42577,
"timestamp": 1438975412.19272
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40052,
"timestamp": 1438975415.681516
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40053,
"timestamp": 1438975415.695585
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40054,
"timestamp": 1438975415.784149
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40055,
"timestamp": 1438975415.784384
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42938,
"timestamp": 1438975417.285877
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42939,
"timestamp": 1438975417.64084
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42940,
"timestamp": 1438975417.846781
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42941,
"timestamp": 1438975418.174816
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42942,
"timestamp": 1438975418.252502
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40062,
"timestamp": 1438975418.728398
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40063,
"timestamp": 1438975418.735892
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40066,
"timestamp": 1438975418.912281
},
{
"destination_ip": "192.168.0.30",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 42949,
"timestamp": 1438975418.953542
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40069,
"timestamp": 1438975418.970933
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40071,
"timestamp": 1438975419.160157
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40072,
"timestamp": 1438975419.167724
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40074,
"timestamp": 1438975419.363007
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40075,
"timestamp": 1438975419.370242
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40077,
"timestamp": 1438975419.563502
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40078,
"timestamp": 1438975419.569185
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40080,
"timestamp": 1438975419.763242
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40081,
"timestamp": 1438975419.769096
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40083,
"timestamp": 1438975419.965889
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40084,
"timestamp": 1438975419.972691
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40086,
"timestamp": 1438975420.167612
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40087,
"timestamp": 1438975420.174799
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40089,
"timestamp": 1438975420.370286
},
{
"destination_ip": "192.168.0.21",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 40090,
"timestamp": 1438975420.376324
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33325,
"timestamp": 1438975627.692959
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33329,
"timestamp": 1438975630.680471
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33330,
"timestamp": 1438975630.730156
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33331,
"timestamp": 1438975630.732841
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33333,
"timestamp": 1438975630.929733
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33334,
"timestamp": 1438975630.932921
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33336,
"timestamp": 1438975631.130647
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33337,
"timestamp": 1438975631.134219
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33339,
"timestamp": 1438975631.332171
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33340,
"timestamp": 1438975631.334887
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33342,
"timestamp": 1438975631.533235
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33343,
"timestamp": 1438975631.537026
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33345,
"timestamp": 1438975631.733574
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33346,
"timestamp": 1438975631.735561
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33348,
"timestamp": 1438975631.935475
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33349,
"timestamp": 1438975631.93968
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33351,
"timestamp": 1438975632.137021
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33352,
"timestamp": 1438975632.14068
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33354,
"timestamp": 1438975632.338641
},
{
"destination_ip": "192.168.0.32",
"destination_port": 80,
"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",
"source_ip": "192.168.0.109",
"source_port": 33355,
"timestamp": 1438975632.342897
},
{
"destination_ip": "192.168.0.40",
"destination_port": 443,
"ja3": "768,22-19-10-102-5-4-101-100-99-98-97-96-21-18-9-20-17-8-6-3,,,",
"ja3_digest": "ee0799c323d74129b75b633dcfd41593",
"source_ip": "192.168.0.109",
"source_port": 60089,
"timestamp": 1438975896.044741
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60090,
"timestamp": 1438975896.065098
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60091,
"timestamp": 1438975902.358398
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60096,
"timestamp": 1438975902.358647
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60093,
"timestamp": 1438975902.358653
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60097,
"timestamp": 1438975902.359146
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60098,
"timestamp": 1438975902.35915
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60099,
"timestamp": 1438975902.360151
},
{
"destination_ip": "192.168.0.40",
"destination_port": 443,
"ja3": "769,49169-57-4,,,",
"ja3_digest": "a471a243929edb6640fff17325ed585f",
"source_ip": "192.168.0.109",
"source_port": 60092,
"timestamp": 1438975902.407827
},
{
"destination_ip": "192.168.0.40",
"destination_port": 443,
"ja3": "769,49169-57-4,13172,,",
"ja3_digest": "231f19e6900c1003725de85e988c8136",
"source_ip": "192.168.0.109",
"source_port": 60094,
"timestamp": 1438975902.408066
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60100,
"timestamp": 1438975903.991505
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60101,
"timestamp": 1438975903.994341
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60102,
"timestamp": 1438975904.042925
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60103,
"timestamp": 1438975904.920607
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60104,
"timestamp": 1438975905.278113
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60105,
"timestamp": 1438975905.568019
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60106,
"timestamp": 1438975905.852386
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60107,
"timestamp": 1438975906.11517
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60108,
"timestamp": 1438975906.364325
},
{
"destination_ip": "192.168.0.40",
"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",
"source_ip": "192.168.0.109",
"source_port": 60109,
"timestamp": 1438975906.659688
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50499,
"timestamp": 1438975946.959158
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50539,
"timestamp": 1438975968.774312
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50540,
"timestamp": 1438975969.12589
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50541,
"timestamp": 1438975969.382566
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50542,
"timestamp": 1438975970.064632
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50543,
"timestamp": 1438975970.068296
},
{
"destination_ip": "192.168.0.60",
"destination_port": 443,
"ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
"ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
"source_ip": "192.168.0.109",
"source_port": 50544,
"timestamp": 1438975970.069778
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7298,
"timestamp": 1438976023.165545
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7301,
"timestamp": 1438976028.310661
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7303,
"timestamp": 1438976028.581532
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7304,
"timestamp": 1438976028.601543
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7305,
"timestamp": 1438976028.741557
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7306,
"timestamp": 1438976028.777827
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7307,
"timestamp": 1438976028.998445
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7308,
"timestamp": 1438976028.998754
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7309,
"timestamp": 1438976028.999243
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7310,
"timestamp": 1438976029.028726
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7311,
"timestamp": 1438976029.051919
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7312,
"timestamp": 1438976029.360982
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7323,
"timestamp": 1438976050.268904
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7324,
"timestamp": 1438976050.290868
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7326,
"timestamp": 1438976051.644443
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7327,
"timestamp": 1438976051.66594
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7328,
"timestamp": 1438976051.824233
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7329,
"timestamp": 1438976051.855177
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7330,
"timestamp": 1438976052.082936
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7331,
"timestamp": 1438976052.083183
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7332,
"timestamp": 1438976052.083439
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7333,
"timestamp": 1438976052.111491
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7334,
"timestamp": 1438976052.13963
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7335,
"timestamp": 1438976052.162501
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7341,
"timestamp": 1438976061.491056
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7342,
"timestamp": 1438976061.492075
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7343,
"timestamp": 1438976061.518477
},
{
"destination_ip": "192.168.0.20",
"destination_port": 443,
"ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
"ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
"source_ip": "192.168.0.102",
"source_port": 7344,
"timestamp": 1438976061.540319
}
]
================================================
FILE: ja3_test.go
================================================
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3
import (
"bytes"
"encoding/json"
"io/ioutil"
"testing"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/dreadl0ck/tlsx"
)
var tlsPacket = []byte{
0x68, 0x7f, 0x74, 0xd6, 0x95, 0xc1, 0x00, 0x22, 0x15, 0x63, 0xc9, 0x5a, 0x08, 0x00, 0x45, 0x00,
0x00, 0xe5, 0x0f, 0xf0, 0x40, 0x00, 0x80, 0x06, 0xaf, 0x9d, 0xc0, 0xa8, 0x01, 0x0e, 0x17, 0x17,
0x61, 0xb8, 0xc0, 0xef, 0x01, 0xbb, 0x49, 0x71, 0x37, 0x25, 0x98, 0x36, 0xf3, 0x06, 0x50, 0x18,
0x01, 0x00, 0xfe, 0xf4, 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0xb8, 0x01, 0x00, 0x00, 0xb4, 0x03,
0x03, 0x59, 0xc1, 0x47, 0x24, 0x67, 0x4d, 0x4f, 0x3b, 0x1f, 0xbd, 0x36, 0x75, 0x9b, 0x2a, 0x92,
0x47, 0x45, 0xf6, 0x7b, 0x03, 0x19, 0x09, 0x13, 0x76, 0x7e, 0xfc, 0x7f, 0xc4, 0x22, 0x6b, 0xe8,
0x1a, 0x20, 0xfb, 0xa8, 0x85, 0x7c, 0x99, 0xca, 0x36, 0x12, 0xe7, 0x72, 0xb0, 0x87, 0xef, 0xdd,
0x08, 0x02, 0x7f, 0xc7, 0x55, 0x72, 0xdc, 0x5a, 0xeb, 0x7b, 0x1f, 0x04, 0x61, 0xf4, 0xa6, 0x82,
0x18, 0x51, 0x00, 0x2a, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x05, 0x00, 0x0a,
0xc0, 0x27, 0xc0, 0x13, 0xc0, 0x14, 0xc0, 0x2b, 0xc0, 0x23, 0xc0, 0x2c, 0xc0, 0x24, 0xc0, 0x09,
0xc0, 0x0a, 0x00, 0x40, 0x00, 0x32, 0x00, 0x6a, 0x00, 0x38, 0x00, 0x13, 0x00, 0x04, 0x01, 0x00,
0x00, 0x41, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x00, 0x0f,
0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2e, 0x6b, 0x72, 0x78, 0x64, 0x2e, 0x6e, 0x65, 0x74, 0x00,
0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
0x0d, 0x00, 0x10, 0x00, 0x0e, 0x04, 0x01, 0x05, 0x01, 0x02, 0x01, 0x04, 0x03, 0x05, 0x03, 0x02,
0x03, 0x02, 0x02,
}
func getHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ClientHelloBasic) {
if tl := p.TransportLayer(); tl != nil {
if tcp, ok := tl.(*layers.TCP); ok {
if tcp.SYN {
b.Fatal("Connection setup")
} else if tcp.FIN {
b.Fatal("Connection teardown")
} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
b.Fatal("Acknowledgement packet")
} else if tcp.RST {
b.Fatal("Unexpected packet")
} else {
hello = &tlsx.ClientHelloBasic{}
err := hello.Unmarshal(tcp.LayerPayload())
if err != nil {
b.Fatal("invalid packet", err)
}
}
}
}
return
}
/*
* Tests
*/
func TestDigestHexCorrect(t *testing.T) {
p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
if p.ErrorLayer() != nil {
t.Fatal(p.ErrorLayer().Error())
}
hash := DigestHexPacket(p)
if hash != "4d7a28d6f2263ed61de88ca66eb011e3" {
t.Fatal(hash, "!=", "4d7a28d6f2263ed61de88ca66eb011e3")
}
}
func TestDigestHexComparePcap(t *testing.T) {
// Step 1: Read JSON output of reference implementation
data, err := ioutil.ReadFile("ja3_output_testpcap.json")
if err != nil {
t.Fatal(err)
}
var referenceRecords = make([]*Record, 0)
err = json.Unmarshal(data, &referenceRecords)
if err != nil {
t.Fatal(err)
}
// Step 2: generate JSON output wth goja3 and read into buffer
var (
b bytes.Buffer
records = make([]*Record, 0)
)
// read test.pcap and generate fingerprints
ReadFileJSON("test.pcap", &b, false)
err = json.Unmarshal(b.Bytes(), &records)
if err != nil {
t.Fatal(err)
}
// Step 3: compare the number of fingerprints and then the digest for each record
if len(records) != len(referenceRecords) {
t.Fatal("len(records) != len(referenceRecords): ", len(records), " != ", len(referenceRecords))
}
// range reference records
for i, r := range referenceRecords {
// compare reference digest to the one produced by goja3
if r.JA3Digest != records[i].JA3Digest {
t.Fatal("r.JA3Digest != records[i].JA3Digest: ", r.JA3Digest, " != ", records[i].JA3Digest)
}
// compare reference digest to the one produced by goja3
if r.JA3SDigest != records[i].JA3SDigest {
t.Fatal("r.JA3SDigest != records[i].JAS3Digest: ", r.JA3SDigest, " != ", records[i].JA3SDigest)
}
}
}
/*
* Benchmarks
*/
func BenchmarkDigestHexPacket(b *testing.B) {
p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
DigestHexPacket(p)
}
}
func BenchmarkDigestPacket(b *testing.B) {
p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
b.Fatal(p.ErrorLayer().Error())
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
DigestPacket(p)
}
}
func BenchmarkBarePacket(b *testing.B) {
p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
b.Fatal(p.ErrorLayer().Error())
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
BarePacket(p)
}
}
func BenchmarkDigestHex(b *testing.B) {
var (
p = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
hello = getHello(p, b)
)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
DigestHex(hello)
}
}
func BenchmarkDigest(b *testing.B) {
var (
p = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
hello = getHello(p, b)
)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
Digest(hello)
}
}
func BenchmarkBare(b *testing.B) {
var (
p = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
hello = getHello(p, b)
)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
Bare(hello)
}
}
================================================
FILE: ja3s.go
================================================
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3
import (
"crypto/md5"
"encoding/hex"
"github.com/dreadl0ck/tlsx"
"strconv"
)
// BareToDigestHex converts a bare []byte to a hex string.
func BareToDigestHexJa3s(bare []byte) string {
sum := md5.Sum(bare)
return hex.EncodeToString(sum[:])
}
// Digest returns only the digest md5.
func DigestJa3s(hello *tlsx.ServerHelloBasic) [md5.Size]byte {
return md5.Sum(BareJa3s(hello))
}
// DigestHex produce md5 hash from bare string.
func DigestHexJa3s(hello *tlsx.ServerHelloBasic) string {
return BareToDigestHex(BareJa3s(hello))
}
// BareJa3s returns the JA3S bare string for a given tlsx.ServerHelloBasic instance
// 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
func BareJa3s(hello *tlsx.ServerHelloBasic) []byte {
// TODO: refactor into struct with inbuilt buffer to reduce allocations to ~ zero
// i.e. only realloc if previously allocated buffer is too small for current packet
var (
maxPossibleBufferLength = 5 + 1 + // Version = uint16 => maximum = 65536 = 5chars + 1 field sep
(5+1)*1 + // CipherSuite = uint16 => maximum = 65536 = 5chars
(5+1)*len(hello.Extensions) // uint16 = 2B => maximum = 65536 = 5chars
buffer = make([]byte, 0, maxPossibleBufferLength)
)
buffer = strconv.AppendInt(buffer, int64(hello.Vers), 10)
buffer = append(buffer, sepFieldByte)
/*
* Cipher Suite
*/
buffer = strconv.AppendInt(buffer, int64(hello.CipherSuite), 10)
buffer = append(buffer, sepFieldByte)
if len(hello.Extensions) > 0 {
/*
* Extensions
*/
// collect extensions
lastElem := len(hello.Extensions) - 1
if len(hello.Extensions) > 1 {
for _, e := range hello.Extensions[:lastElem] {
// filter GREASE values
if !greaseValues[uint16(e)] {
buffer = strconv.AppendInt(buffer, int64(e), 10)
buffer = append(buffer, sepValueByte)
}
}
}
// append last element if extensions are not empty
if lastElem != -1 {
// filter GREASE values
if !greaseValues[uint16(hello.Extensions[lastElem])] {
buffer = strconv.AppendInt(buffer, int64(hello.Extensions[lastElem]), 10)
}
}
}
return buffer
}
================================================
FILE: ja3s_pcap.go
================================================
//go:build !ja3_disable_gopacket
package ja3
import (
"fmt"
"io"
"strings"
"github.com/gopacket/gopacket"
)
// ReadFileJa3s reads the PCAP file at the given path
// and prints out all packets containing JA3S digests to the supplied io.Writer
func ReadFileJa3s(file string, out io.Writer) {
r, f, link, err := openPcap(file)
if err != nil {
panic(err)
}
defer f.Close()
count := 0
for {
// read packet data
data, _, err := r.ReadPacketData()
if err == io.EOF {
if Debug {
fmt.Println(count, "fingerprints.")
}
return
} else if err != nil {
panic(err)
}
var (
// create gopacket
p = gopacket.NewPacket(data, link, gopacket.Lazy)
// get JA3 if possible
digest = DigestHexPacketJa3s(p)
)
// check if we got a result
if digest != "" {
count++
var (
b strings.Builder
nl = p.NetworkLayer()
tl = p.TransportLayer()
)
// got an a digest but no transport or network layer
if tl == nil || nl == nil {
if Debug {
fmt.Println("got a nil layer: ", nl, tl, p.Dump(), digest)
}
continue
}
b.WriteString("[")
b.WriteString(nl.NetworkFlow().Dst().String())
b.WriteString(":")
b.WriteString(tl.TransportFlow().Dst().String())
b.WriteString("] JA3S: ")
b.WriteString(string(BarePacketJa3s(p)))
b.WriteString(" --> ")
b.WriteString(digest)
b.WriteString("\n")
_, err := out.Write([]byte(b.String()))
if err != nil {
panic(err)
}
}
}
}
================================================
FILE: ja3s_test.go
================================================
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3_test
import (
"bytes"
"encoding/hex"
"github.com/dreadl0ck/ja3"
"io/ioutil"
"log"
"testing"
"github.com/dreadl0ck/tlsx"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
)
var tlsServerHelloPacket, errDecodeServerHelloPacket = hex.DecodeString("f018982a38be48d343aac4b80800450005b4447b00007906ea20225f7893c0a8b20d01bbefca6011d3eb8013192d801000f087ae00000101080a79facb3a56d9798616030300640200006003035e5bea4420be035069306e15e36eca187d56a201e8e96f0a1afeeb529413fba020746572b9b8832a5b8f236ba51743281c20e923a880067e4c38a754f53f3a8743c02f00001800170000ff01000100000b0002010000100005000302683216030309980b0009940009910004fc308204f8308203e0a00302010202100a86b904765831e240cc6211101f5736300d06092a864886f70d01010b0500305e310b300906035504061302555331153013060355040a130c446967694365727420496e6331193017060355040b13107777772e64696769636572742e636f6d311d301b0603550403131447656f5472757374205253412043412032303138301e170d3138303130343030303030305a170d3230303730393132303030305a3068310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f060355040713085061736164656e61311b3019060355040a13124f70656e5820546563686e6f6c6f676965733114301206035504030c0b2a2e6f70656e782e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100bf0efabfe55538e687d4db8f8d615ab1d5dce14702307ca21c0547cd2cc43e455627b4d8decd8015296c19127d29025fbac71f2fc1af1bfe525ed0c2778028a3e7dd643b60842801d9d196651924057827c4d3ce6ae62253b869f384d56a72060055e3537a67a9904e61a4628067f07c680a8bf30c14444d090910c182ea3c5e473b1ab1233838645b0bdfb24265ee32e3521b4bdbae6d79acdbdd7a00c0f00479537079fd352aade5e8394be2f2b397366cfd64bb94b1b96515b4e78d3a2fac04e9d5787792fcf411b6b66cb5dfbc4cb023d93fa99cee630d8ed3558b33e0f2a019ecc5eac16919c48a844f693b5e7ccb4eb84724ab963979c570eb7a3b58ad0203010001a38201a6308201a2301f0603551d230418301680149058ffb09c75a8515477b1edf2a34316389e6cc5301d0603551d0e04160414e03952aaaeb23c67e939ec6022188686bcb314c430210603551d11041a3018820b2a2e6f70656e782e6e657482096f70656e782e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302303e0603551d1f043730353033a031a02f862d687474703a2f2f6364702e67656f74727573742e636f6d2f47656f54727573745253414341323031382e63726c304c0603551d2004453043303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f4350533008060667810c010202307506082b0601050507010104693067302606082b06010505073001861a687474703a2f2f7374617475732e67656f74727573742e636f6d303d06082b060105050730028631687474703a2f2f636163657274732e67656f74727573742e636f6d2f47656f54727573745253414341323031382e63727430090603551d1304023000300d06092a864886f70d01010b0500038201010045a0d33fa95d251c861518096f2ffe89bae2e062cfc4505859b42adeaa775fcf34c2eebde155cfb6449a806abffbaad2e88d77b1397e76ea08265dbc2aa98cb04e3d141c27ed629961d9cd90bb7f6db672408bb15126dd4df4cdf831465c1ee9f321505ace6e7c09d864683fbbaa8ee9fc73e74feefaa5a027067856000f56c9ae39bcb3cc586928429bd8abe5d2a3d8df5c63b4db45fa2cc3ffa6f75b946188019e162fb8dfacf2be3064bf52788378e61c8397ec13e72649fbe15e5c5b6d8bae43ca7b188eda5a41e512f508206fefe96a832ce4676a425ddd06e3cfe93046140818a7359723f2477315f38ab51f83c9dced627efaf6541ed3e9c0767376b200048f3082048b30820373a0")
func getServerHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ServerHelloBasic) {
if errDecodeServerHelloPacket != nil {
log.Fatal("failed to decode test packet", errDecodeServerHelloPacket)
}
if tl := p.TransportLayer(); tl != nil {
if tcp, ok := tl.(*layers.TCP); ok {
if tcp.SYN {
b.Fatal("Connection setup")
} else if tcp.FIN {
b.Fatal("Connection teardown")
} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
b.Fatal("Acknowledgement packet")
} else if tcp.RST {
b.Fatal("Unexpected packet")
} else {
hello = &tlsx.ServerHelloBasic{}
err := hello.Unmarshal(tcp.LayerPayload())
if err != nil {
b.Fatal("invalid packet", err)
}
}
}
}
return
}
/*
* Tests
*/
func TestDigestHexJa3sCorrect(t *testing.T) {
p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
if p.ErrorLayer() != nil {
t.Fatal(p.ErrorLayer().Error())
}
hash := ja3.DigestHexPacketJa3s(p)
if hash != "5b94af9bf6efc9dea416841602004fbb" {
t.Fatal(hash, "!=", "5b94af9bf6efc9dea416841602004fbb")
}
}
func TestPcapWithNonEthernetEncapsulation(t *testing.T) {
var (
b bytes.Buffer
)
// read test2.pcap and generate fingerprints
ja3.ReadFileJSON("test2.pcap", &b, true)
if b.String() == "" {
t.Fatal("expected output")
}
}
func TestDigestHexJa3sComparePcap(t *testing.T) {
// Step 1: Read output of reference implementation
data, err := ioutil.ReadFile("ja3s_test.log")
if err != nil {
t.Fatal(err)
}
// Step 2: generate output wth goja3 and read into buffer
var (
b bytes.Buffer
)
// read test.pcap and generate fingerprints
ja3.ReadFileJa3s("test.pcap", &b)
// Step 3: compare the number of fingerprints and then the digest for each record
if len(data) != len(b.Bytes()) {
t.Fatal("len(records) != len(referenceRecords): ", len(data), " != ", len(b.Bytes()))
}
reference := bytes.Split(data, []byte{'\n'})
// range reference records
for i, r := range bytes.Split(b.Bytes(), []byte{'\n'}) {
// compare reference digest to the one produced by goja3
if string(r) != string(reference[i]) {
t.Fatal("message does not match reference implementation: ", string(r), " != ", string(reference[i]))
}
}
}
/*
* Benchmarks
*/
func BenchmarkDigestHexPacketJa3s(b *testing.B) {
p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ja3.DigestHexPacketJa3s(p)
}
}
func BenchmarkDigestPacketJa3s(b *testing.B) {
p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
b.Fatal(p.ErrorLayer().Error())
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ja3.DigestPacketJa3s(p)
}
}
func BenchmarkBarePacketJa3s(b *testing.B) {
p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
b.Fatal(p.ErrorLayer().Error())
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ja3.BarePacketJa3s(p)
}
}
func BenchmarkDigestHexJa3s(b *testing.B) {
var (
p = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
hello = getServerHello(p, b)
)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ja3.DigestHexJa3s(hello)
}
}
func BenchmarkDigestJa3s(b *testing.B) {
var (
p = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
hello = getServerHello(p, b)
)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ja3.DigestJa3s(hello)
}
}
func BenchmarkBareJa3s(b *testing.B) {
var (
p = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
hello = getServerHello(p, b)
)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
ja3.BareJa3s(hello)
}
}
================================================
FILE: ja3s_test.log
================================================
[192.168.0.104:47851] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d
[192.168.0.104:47852] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:47436] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47437] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47438] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47439] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47440] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47441] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47442] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42577] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42938] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42939] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42940] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42941] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42942] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42949] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:60089] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d
[192.168.0.109:60090] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60093] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60096] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60097] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60098] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60099] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60091] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60094] JA3S: 769,4, --> 53611273a714cb4789c8222932efd5a7
[192.168.0.109:60092] JA3S: 769,4, --> 53611273a714cb4789c8222932efd5a7
[192.168.0.109:60100] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60101] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60102] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60103] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60104] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60105] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60106] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60107] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60108] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60109] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:50499] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50539] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50540] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50541] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50542] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50543] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50544] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.102:7298] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7301] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7303] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7304] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7305] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7306] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7307] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7308] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7309] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7310] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7311] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7312] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7323] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7324] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7326] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7327] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7328] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7329] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7330] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7331] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7332] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7333] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7334] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7335] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7341] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7342] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7343] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7344] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
================================================
FILE: json.go
================================================
//go:build !ja3_disable_gopacket
/*
* JA3 - TLS Client Hello Hash
* Copyright (c) 2017, Salesforce.com, Inc.
* this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ja3
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"strconv"
"time"
"github.com/gopacket/gopacket"
)
// Record contains all information for a calculated JA3
type Record struct {
DestinationIP string `json:"destination_ip"`
DestinationPort int `json:"destination_port"`
JA3 string `json:"ja3"`
JA3Digest string `json:"ja3_digest"`
JA3S string `json:"ja3s"`
JA3SDigest string `json:"ja3s_digest"`
SourceIP string `json:"source_ip"`
SourcePort int `json:"source_port"`
Timestamp float64 `json:"timestamp"`
}
// ReadFileJSON reads the PCAP file at the given path
// and prints out all packets containing JA3 digests formatted as JSON to the supplied io.Writer
func ReadFileJSON(file string, out io.Writer, doJA3s bool) {
r, f, link, err := openPcap(file)
if err != nil {
panic(err)
}
defer f.Close()
var records []*Record
for {
// read packet data
data, ci, err := r.ReadPacketData()
if err == io.EOF {
break
} else if err != nil {
panic(err)
}
var (
// create gopacket
p = gopacket.NewPacket(data, link, gopacket.Lazy)
// get JA3 if possible
bare = BarePacket(p)
isServer bool
)
if doJA3s && len(bare) == 0 {
bare = BarePacketJa3s(p)
isServer = true
}
// check if we got a result
if len(bare) > 0 {
var (
nl = p.NetworkLayer()
tl = p.TransportLayer()
)
// stop if either network or transport layer is nil
if tl == nil || nl == nil {
fmt.Println("error: ", nl, tl, p.Dump())
continue
}
r := &Record{
DestinationIP: nl.NetworkFlow().Dst().String(),
DestinationPort: int(binary.BigEndian.Uint16(tl.TransportFlow().Dst().Raw())),
SourceIP: nl.NetworkFlow().Src().String(),
SourcePort: int(binary.BigEndian.Uint16(tl.TransportFlow().Src().Raw())),
Timestamp: timeToFloat(ci.Timestamp),
}
if isServer {
r.JA3S = string(bare)
r.JA3SDigest = BareToDigestHex(bare)
} else {
r.JA3 = string(bare)
r.JA3Digest = BareToDigestHex(bare)
}
// append record and populate all fields
records = append(records, r)
}
}
// make it pretty please
b, err := json.MarshalIndent(records, "", " ")
if err != nil {
panic(err)
}
if string(b) != "null" { // no matches will result in "null" json
// write to output io.Writer
_, err = out.Write(b)
if err != nil {
panic(err)
}
}
if Debug {
fmt.Println(len(records), "fingerprints.")
}
}
// convert a time.Time to a string timestamp in the format seconds.microseconds
func timeToString(t time.Time) string {
micro := fmt.Sprintf("%06d", t.Nanosecond()/1000)
return strconv.FormatInt(t.Unix(), 10) + "." + micro
}
func timeStringToFloat64(t string) float64 {
f, err := strconv.ParseFloat(t, 64)
if err != nil {
fmt.Println("[ERROR] failed to convert", t, "to float64. error:", err)
}
return f
}
func timeToFloat(t time.Time) float64 {
return timeStringToFloat64(timeToString(t))
}
================================================
FILE: live.go
================================================
package ja3
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/gopacket/gopacket/pcap"
)
// ReadInterface reads packets from the named interface
// if asJSON is true the results will be dumped as newline separated JSON objects
// otherwise CSV will be printed to the supplied io.Writer.
func ReadInterface(iface string, out io.Writer, separator string, ja3s bool, asJSON bool, snaplen int, promisc bool, timeout time.Duration) {
h, err := pcap.OpenLive(iface, int32(snaplen), promisc, timeout)
if err != nil {
panic(err)
}
defer h.Close()
if !asJSON {
columns := []string{"timestamp", "source_ip", "source_port", "destination_ip", "destination_port", "ja3_digest"}
_, err = out.Write([]byte(strings.Join(columns, separator) + "\n"))
if err != nil {
panic(err)
}
}
count := 0
for {
// read packet data
data, ci, err := h.ReadPacketData()
if err == io.EOF {
if Debug {
fmt.Println(count, "fingerprints.")
}
return
} else if err != nil {
panic(err)
}
var (
// create gopacket
p = gopacket.NewPacket(data, layers.LinkTypeEthernet, gopacket.Lazy)
bare = BarePacket(p)
isServer bool
)
if ja3s && len(bare) == 0 {
bare = BarePacketJa3s(p)
isServer = true
}
// check if we got a result
if len(bare) > 0 {
count++
var (
b strings.Builder
nl = p.NetworkLayer()
tl = p.TransportLayer()
)
// got a bare but no transport or network layer
if tl == nil || nl == nil {
if Debug {
fmt.Println("got a nil layer: ", nl, tl, p.Dump(), string(bare))
}
continue
}
r := &Record{
DestinationIP: nl.NetworkFlow().Dst().String(),
DestinationPort: int(binary.BigEndian.Uint16(tl.TransportFlow().Dst().Raw())),
SourceIP: nl.NetworkFlow().Src().String(),
SourcePort: int(binary.BigEndian.Uint16(tl.TransportFlow().Src().Raw())),
Timestamp: timeToFloat(ci.Timestamp),
}
digest := BareToDigestHex(bare)
if isServer {
r.JA3S = string(bare)
r.JA3SDigest = digest
} else {
r.JA3 = string(bare)
r.JA3Digest = digest
}
if asJSON {
// make it pretty please
b, err := json.MarshalIndent(r, "", " ")
if err != nil {
panic(err)
}
if string(b) != "null" { // no matches will result in "null" json
// write to output io.Writer
_, err = out.Write(b)
if err != nil {
panic(err)
}
_, err = out.Write([]byte("\n"))
if err != nil {
panic(err)
}
}
} else { // CSV
b.WriteString(timeToString(ci.Timestamp))
b.WriteString(separator)
b.WriteString(nl.NetworkFlow().Src().String())
b.WriteString(separator)
b.WriteString(tl.TransportFlow().Src().String())
b.WriteString(separator)
b.WriteString(nl.NetworkFlow().Dst().String())
b.WriteString(separator)
b.WriteString(tl.TransportFlow().Dst().String())
b.WriteString(separator)
b.WriteString(digest)
b.WriteString("\n")
_, err := out.Write([]byte(b.String()))
if err != nil {
panic(err)
}
}
}
}
}
================================================
FILE: utils.go
================================================
package ja3
import (
"fmt"
"github.com/gopacket/gopacket/layers"
"os"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/pcapgo"
)
// PacketSource means we can read Packets.
type PacketSource interface {
ReadPacketData() ([]byte, gopacket.CaptureInfo, error)
}
func openPcap(file string) (PacketSource, *os.File, layers.LinkType, error) {
// get file handle
f, err := os.Open(file)
if err != nil {
panic(err)
}
var (
reader PacketSource
linkType layers.LinkType
)
// try to create pcap reader
pcapReader, errPcap := pcapgo.NewReader(f)
if errPcap != nil {
// maybe its a PCAPNG
// reopen file, otherwise offsets will be wrong
err = f.Close()
if err != nil {
panic(err)
}
f, err = os.Open(file)
if err != nil {
panic(err)
}
// try to create pcapng reader
ngReader, errPcapNg := pcapgo.NewNgReader(f, pcapgo.DefaultNgReaderOptions)
if errPcapNg != nil {
// nope
fmt.Println("pcap error:", errPcap)
fmt.Println("pcap-ng error:", errPcapNg)
panic("cannot open PCAP file")
}
linkType = ngReader.LinkType()
reader = ngReader
} else {
linkType = pcapReader.LinkType()
reader = pcapReader
}
return reader, f, linkType, err
}
gitextract_5b0w5x7_/ ├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── TODO.md ├── cmd/ │ └── main.go ├── csv.go ├── go.mod ├── go.sum ├── gopacket.go ├── install.sh ├── ja3.go ├── ja3_output_testpcap.json ├── ja3_test.go ├── ja3s.go ├── ja3s_pcap.go ├── ja3s_test.go ├── ja3s_test.log ├── json.go ├── live.go ├── test.pcap ├── test2.pcap └── utils.go
SYMBOL INDEX (45 symbols across 11 files)
FILE: cmd/main.go
constant version (line 42) | version = "v1.0.2"
function main (line 44) | func main() {
FILE: csv.go
function ReadFileCSV (line 29) | func ReadFileCSV(file string, out io.Writer, separator string, doJA3s bo...
FILE: gopacket.go
function DigestPacket (line 30) | func DigestPacket(p gopacket.Packet) [md5.Size]byte {
function DigestPacketJa3s (line 37) | func DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte {
function DigestHexPacket (line 43) | func DigestHexPacket(p gopacket.Packet) string {
function DigestHexPacketJa3s (line 56) | func DigestHexPacketJa3s(p gopacket.Packet) string {
function BarePacket (line 69) | func BarePacket(p gopacket.Packet) []byte {
function BarePacketJa3s (line 104) | func BarePacketJa3s(p gopacket.Packet) []byte {
FILE: ja3.go
function BareToDigestHex (line 47) | func BareToDigestHex(bare []byte) string {
function Digest (line 53) | func Digest(hello *tlsx.ClientHelloBasic) [md5.Size]byte {
function DigestHex (line 58) | func DigestHex(hello *tlsx.ClientHelloBasic) string {
function Bare (line 76) | func Bare(hello *tlsx.ClientHelloBasic) []byte {
FILE: ja3_test.go
function getHello (line 47) | func getHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ClientHelloB...
function TestDigestHexCorrect (line 74) | func TestDigestHexCorrect(t *testing.T) {
function TestDigestHexComparePcap (line 87) | func TestDigestHexComparePcap(t *testing.T) {
function BenchmarkDigestHexPacket (line 138) | func BenchmarkDigestHexPacket(b *testing.B) {
function BenchmarkDigestPacket (line 149) | func BenchmarkDigestPacket(b *testing.B) {
function BenchmarkBarePacket (line 162) | func BenchmarkBarePacket(b *testing.B) {
function BenchmarkDigestHex (line 175) | func BenchmarkDigestHex(b *testing.B) {
function BenchmarkDigest (line 188) | func BenchmarkDigest(b *testing.B) {
function BenchmarkBare (line 202) | func BenchmarkBare(b *testing.B) {
FILE: ja3s.go
function BareToDigestHexJa3s (line 25) | func BareToDigestHexJa3s(bare []byte) string {
function DigestJa3s (line 31) | func DigestJa3s(hello *tlsx.ServerHelloBasic) [md5.Size]byte {
function DigestHexJa3s (line 36) | func DigestHexJa3s(hello *tlsx.ServerHelloBasic) string {
function BareJa3s (line 44) | func BareJa3s(hello *tlsx.ServerHelloBasic) []byte {
FILE: ja3s_pcap.go
function ReadFileJa3s (line 15) | func ReadFileJa3s(file string, out io.Writer) {
FILE: ja3s_test.go
function getServerHello (line 32) | func getServerHello(p gopacket.Packet, b *testing.B) (hello *tlsx.Server...
function TestDigestHexJa3sCorrect (line 64) | func TestDigestHexJa3sCorrect(t *testing.T) {
function TestPcapWithNonEthernetEncapsulation (line 77) | func TestPcapWithNonEthernetEncapsulation(t *testing.T) {
function TestDigestHexJa3sComparePcap (line 90) | func TestDigestHexJa3sComparePcap(t *testing.T) {
function BenchmarkDigestHexPacketJa3s (line 127) | func BenchmarkDigestHexPacketJa3s(b *testing.B) {
function BenchmarkDigestPacketJa3s (line 138) | func BenchmarkDigestPacketJa3s(b *testing.B) {
function BenchmarkBarePacketJa3s (line 151) | func BenchmarkBarePacketJa3s(b *testing.B) {
function BenchmarkDigestHexJa3s (line 164) | func BenchmarkDigestHexJa3s(b *testing.B) {
function BenchmarkDigestJa3s (line 177) | func BenchmarkDigestJa3s(b *testing.B) {
function BenchmarkBareJa3s (line 191) | func BenchmarkBareJa3s(b *testing.B) {
FILE: json.go
type Record (line 31) | type Record struct
function ReadFileJSON (line 45) | func ReadFileJSON(file string, out io.Writer, doJA3s bool) {
function timeToString (line 133) | func timeToString(t time.Time) string {
function timeStringToFloat64 (line 138) | func timeStringToFloat64(t string) float64 {
function timeToFloat (line 146) | func timeToFloat(t time.Time) float64 {
FILE: live.go
function ReadInterface (line 19) | func ReadInterface(iface string, out io.Writer, separator string, ja3s b...
FILE: utils.go
type PacketSource (line 13) | type PacketSource interface
function openPcap (line 17) | func openPcap(file string) (PacketSource, *os.File, layers.LinkType, err...
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (140K chars).
[
{
"path": ".gitignore",
"chars": 6,
"preview": ".idea/"
},
{
"path": "AUTHORS",
"chars": 51,
"preview": "Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>"
},
{
"path": "LICENSE",
"chars": 1483,
"preview": "Copyright (c) 2017, Salesforce.com, Inc. \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with"
},
{
"path": "README.md",
"chars": 13232,
"preview": "# JA3\n\n[](https://goreportcard.com/report/gith"
},
{
"path": "TODO.md",
"chars": 97,
"preview": "# TODO\n\n- compare number of records to output of python implementation\n- 0 allocations for Bare()"
},
{
"path": "cmd/main.go",
"chars": 2584,
"preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
},
{
"path": "csv.go",
"chars": 2832,
"preview": "//go:build !ja3_disable_gopacket\n\n/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this "
},
{
"path": "go.mod",
"chars": 257,
"preview": "module github.com/dreadl0ck/ja3\n\nrequire (\n\tgithub.com/dreadl0ck/tlsx v1.1.1\n\tgithub.com/gopacket/gopacket v1.4.0\n)\n\nreq"
},
{
"path": "go.sum",
"chars": 1219,
"preview": "github.com/dreadl0ck/tlsx v1.1.1 h1:fclhQwhO2HmRm568JFBEX0G6S7hFGBsN/JBFh9RqgVU=\ngithub.com/dreadl0ck/tlsx v1.1.1/go.mod"
},
{
"path": "gopacket.go",
"chars": 3482,
"preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
},
{
"path": "install.sh",
"chars": 83,
"preview": "#!/bin/bash\n\ngo build -o $(go env GOPATH)/bin/goja3 -i github.com/dreadl0ck/ja3/cmd"
},
{
"path": "ja3.go",
"chars": 6251,
"preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
},
{
"path": "ja3_output_testpcap.json",
"chars": 67850,
"preview": "[\n {\n \"destination_ip\": \"192.168.0.20\",\n \"destination_port\": 443,\n \"ja3\": \"768,22-19-10-102-5-4-"
},
{
"path": "ja3_test.go",
"chars": 6112,
"preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
},
{
"path": "ja3s.go",
"chars": 2911,
"preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
},
{
"path": "ja3s_pcap.go",
"chars": 1477,
"preview": "//go:build !ja3_disable_gopacket\n\npackage ja3\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/gopacket/gopacket\"\n)\n\n// R"
},
{
"path": "ja3s_test.go",
"chars": 7796,
"preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
},
{
"path": "ja3s_test.log",
"chars": 5135,
"preview": "[192.168.0.104:47851] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d\n[192.168.0.104:47852] JA3S: 769,5, --> 9aeeb849"
},
{
"path": "json.go",
"chars": 3729,
"preview": "//go:build !ja3_disable_gopacket\n\n/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this "
},
{
"path": "live.go",
"chars": 3190,
"preview": "package ja3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gopacket/gopacke"
},
{
"path": "utils.go",
"chars": 1222,
"preview": "package ja3\n\nimport (\n\t\"fmt\"\n\t\"github.com/gopacket/gopacket/layers\"\n\t\"os\"\n\n\t\"github.com/gopacket/gopacket\"\n\t\"github.com/"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the dreadl0ck/ja3 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (127.9 KB), approximately 54.6k tokens, and a symbol index with 45 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.