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 ================================================ 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 [![Go Report Card](https://goreportcard.com/badge/github.com/dreadl0ck/ja3)](https://goreportcard.com/report/github.com/dreadl0ck/ja3) [![License](https://img.shields.io/badge/License-BSDv3-blue.svg)](https://raw.githubusercontent.com/dreadl0ck/ja3/master/docs/LICENSE) [![Golang](https://img.shields.io/badge/Go-1.10-blue.svg)](https://golang.org) ![Linux](https://img.shields.io/badge/Supports-Linux-green.svg) ![macOS](https://img.shields.io/badge/Supports-macOS-green.svg) ![Windows](https://img.shields.io/badge/Supports-Windows-green.svg) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](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 * * 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 * * 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 * * 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 * * 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 * * 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 * * 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 * * 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 * * 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 }