diff --git a/README.md b/README.md index 4b53256..bd5d1fd 100755 --- a/README.md +++ b/README.md @@ -38,9 +38,9 @@ After that, just run the binary (`sudo ./TrackMe`) The site returns 3 different fingerprints: the [JA3](https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967/), a TLS fingerprint, an HTTP/2 ["akamai-fingerprint"](https://www.blackhat.com/docs/eu-17/materials/eu-17-Shuster-Passive-Fingerprinting-Of-HTTP2-Clients-wp.pdf) (Only works on HTTP/2 connections) and my own custom "PeetPrint". -### Custom Fingerpint ("PeetPrint") +### Custom Fingerprint ("PeetPrint") -I wanted to extend JA3, so I created my own TLS fingerprint algorithm. It's better suited for fingerprinting TLS1.3 connections, because [JA3 doesn't really do that well](https://github.com/salesforce/ja3/issues/78), and has more datapoints. The designed is inspired by the http/2 fingerprint proposed by akamai. +I wanted to extend JA3, so I created my own TLS fingerprint algorithm. It's better suited for fingerprinting TLS1.3 connections, because [JA3 doesn't really do that well](https://github.com/salesforce/ja3/issues/78), and has more datapoints. The design is inspired by the http/2 fingerprint proposed by akamai. It looks like this: @@ -48,21 +48,21 @@ It looks like this: supported-tls-versions|supported-protocols|supported-groups|supported-signature-algorithms|psk-key-exchange-mode|certificate-compression-algorithms|cipher-suites|sorted-extensions ``` -"-" is used as the seperator. +"-" is used as the separator. -**supported-tls-versions**: Seperated list of supported TLS versions as sent in the `supported_versions` extension. +**supported-tls-versions**: Separated list of supported TLS versions as sent in the `supported_versions` extension. -**supported-protocols**: Seperated list of supported HTTP versions as sent in the `application_layer_protocol_negotiation` extension. http/1.0 => 1.0, http/1.1 => 1.1, http/2 => 2 +**supported-protocols**: Separated list of supported HTTP versions as sent in the `application_layer_protocol_negotiation` extension. http/1.0 => 1.0, http/1.1 => 1.1, http/2 => 2 -**supported-groups**: Seperated list of supported elliptic curve groups as sent in the `supported_groups` extension. +**supported-groups**: Separated list of supported elliptic curve groups as sent in the `supported_groups` extension. -**supported-signature-algorithms**: Seperated list of supported signatue algorithms as sent in the `signature_algorithms` extension. +**supported-signature-algorithms**: Separated list of supported signature algorithms as sent in the `signature_algorithms` extension. **psk-key-exchange-mode** The PSK key exchange mode as specified in the `psk_key_exchange_modes` extension. Usually 0 or 1. -**certificate-compression-algorithms** Seperated list of the certificate compression algorithms as sent in the `compress_certificate` extension. +**certificate-compression-algorithms** Separated list of the certificate compression algorithms as sent in the `compress_certificate` extension. -**cipher-suites**: Seperated list of the supported cipher suites. +**cipher-suites**: Separated list of the supported cipher suites. **sorted-extensions**: Sorted list of the supported extensions. (Sorted because of order randomization used by chrome) @@ -80,7 +80,7 @@ The site exposes a lot of different API endpoints. ### /api/all -Returns all of the collected data about an request +Returns all of the collected data about a request When packet capture is enabled with `device`, `tcpip` also contains the initial inbound TCP SYN as `tcp_syn` and a raw p0f-format signature as @@ -122,7 +122,7 @@ You can also run the server in a docker container using docker-compose. ```bash # generate certs and update your config.json -docker-compose -up --build +docker-compose up --build # visit https://localhost/api/all ``` diff --git a/pkg/server/connection_handler.go b/pkg/server/connection_handler.go index e40f44a..b75f9d5 100755 --- a/pkg/server/connection_handler.go +++ b/pkg/server/connection_handler.go @@ -129,8 +129,8 @@ func parseHTTP2(f *http2.Framer, c chan types.ParsedFrame) { } for _, h := range h2Headers { - headerStr := fmt.Sprintf("%s: %s", h.Name, h.Value) - p.Headers = append(p.Headers, headerStr) + headerStr := fmt.Sprintf("%s: %s", h.Name, h.Value) + p.Headers = append(p.Headers, headerStr) } if frame.HasPriority() { prio := types.Priority{} @@ -169,7 +169,7 @@ func parseHTTP2(f *http2.Framer, c chan types.ParsedFrame) { func (srv *Server) HandleTLSConnection(conn net.Conn) error { // Read the first line of the request // We only read the first line to determine if the connection is HTTP1 or HTTP2 - // If we know that it isnt HTTP2, we can read the rest of the request and then start processing it + // If we know that it is not HTTP2, we can read the rest of the request and then start processing it // If we know that it is HTTP2, we start the HTTP2 handler l := len([]byte(HTTP2_PREAMBLE)) @@ -198,18 +198,18 @@ func (srv *Server) HandleTLSConnection(conn net.Conn) error { rawB64 := base64.StdEncoding.EncodeToString(rawBytes) tlsDetails := types.TLSDetails{ - Ciphers: JA3Data.ReadableCiphers, - Extensions: parsedClientHello.Extensions, - RecordVersion: JA3Data.Version, - NegotiatedVesion: fmt.Sprintf("%v", conn.(*utls.Conn).ConnectionState().Version), - JA3: JA3Data.JA3, - JA3Hash: JA3Data.JA3Hash, - PeetPrint: peetfp, - PeetPrintHash: peetprintHash, - SessionID: parsedClientHello.SessionID, - ClientRandom: parsedClientHello.ClientRandom, - RawBytes: hs, - RawB64: rawB64, + Ciphers: JA3Data.ReadableCiphers, + Extensions: parsedClientHello.Extensions, + RecordVersion: JA3Data.Version, + NegotiatedVersion: fmt.Sprintf("%v", conn.(*utls.Conn).ConnectionState().Version), + JA3: JA3Data.JA3, + JA3Hash: JA3Data.JA3Hash, + PeetPrint: peetfp, + PeetPrintHash: peetprintHash, + SessionID: parsedClientHello.SessionID, + ClientRandom: parsedClientHello.ClientRandom, + RawBytes: hs, + RawB64: rawB64, } // Check if the first line is HTTP/2 @@ -449,18 +449,18 @@ func (srv *Server) HandleHTTP3() http.Handler { rawB64 := base64.StdEncoding.EncodeToString(h3state.ClientHello) tlsDetails = &types.TLSDetails{ - Ciphers: JA3Data.ReadableCiphers, - Extensions: parsedClientHello.Extensions, - RecordVersion: JA3Data.Version, - NegotiatedVesion: fmt.Sprintf("%v", h3state.TLS.Version), - JA3: JA3Data.JA3, - JA3Hash: JA3Data.JA3Hash, - PeetPrint: peetfp, - PeetPrintHash: peetprintHash, - SessionID: parsedClientHello.SessionID, - ClientRandom: parsedClientHello.ClientRandom, - RawBytes: clientHelloHex, - RawB64: rawB64, + Ciphers: JA3Data.ReadableCiphers, + Extensions: parsedClientHello.Extensions, + RecordVersion: JA3Data.Version, + NegotiatedVersion: fmt.Sprintf("%v", h3state.TLS.Version), + JA3: JA3Data.JA3, + JA3Hash: JA3Data.JA3Hash, + PeetPrint: peetfp, + PeetPrintHash: peetprintHash, + SessionID: parsedClientHello.SessionID, + ClientRandom: parsedClientHello.ClientRandom, + RawBytes: clientHelloHex, + RawB64: rawB64, } } diff --git a/pkg/tls/fingerprint_tls.go b/pkg/tls/fingerprint_tls.go index 9ee5b01..a17f31c 100755 --- a/pkg/tls/fingerprint_tls.go +++ b/pkg/tls/fingerprint_tls.go @@ -124,12 +124,12 @@ func CalculateJA3(parsed ClientHello) JA3Calculating { return j } -func joinInts(ints []int, seperator string) string { +func joinInts(ints []int, separator string) string { tmp := []string{} for _, v := range ints { tmp = append(tmp, fmt.Sprintf("%v", v)) } - return strings.Join(tmp, seperator) + return strings.Join(tmp, separator) } // same as joinInts but flags GREASE values, like we already do for ciphers/extensions/groups @@ -170,19 +170,19 @@ func CalculatePeetPrint(parsed ClientHello, j JA3Calculating) (string, string) { // Sort extensions because the order is randomized sort.Strings(j.PeetPrintExtensions) - tls_versions := strings.Join(versions, "-") // Comma seperated list of supported TLS versions as sent in the `supported_versions` extension. TODO - protos := strings.Join(tmp, "-") // Comma seperated list of supported HTTP versions as sent in the `application_layer_protocol_negotiation` extension. http/1.0 => 1.0, http/1.1 => 1.1, http/2 => 2 - sig_als := joinSignatureAlgorithms(parsed.SignatureAlgorithms) // Comma seperated list of supported signatue algorithms as sent in the `signature_algorithms` extension. - key_mode := fmt.Sprintf("%v", parsed.PSKKeyExchangeMode) // The PSK key exchange mode as specified in the`psk_key_exchange_modes` extension. Usually 0 or 1. - comp_algs := joinInts(parsed.CertCompressionAlgorithms, "-") // Comma seperated list of the certificate compression algorithms as sent in the `compress_certificate` extension - groups := strings.Join(j.PeetPrintCurves, "-") // Comma seperated list of supported elliptic curve groups as sent in the `supported_groups` extension. - suites := strings.Join(j.PeetPrintCiphers, "-") // Cipher suites - extensions := strings.Join(j.PeetPrintExtensions, "-") // Extensions + tls_versions := strings.Join(versions, "-") // Comma separated list of supported TLS versions as sent in the `supported_versions` extension. TODO + protos := strings.Join(tmp, "-") // Comma separated list of supported HTTP versions as sent in the `application_layer_protocol_negotiation` extension. http/1.0 => 1.0, http/1.1 => 1.1, http/2 => 2 + sig_algs := joinSignatureAlgorithms(parsed.SignatureAlgorithms) // Comma separated list of supported signature algorithms as sent in the `signature_algorithms` extension. + key_mode := fmt.Sprintf("%v", parsed.PSKKeyExchangeMode) // The PSK key exchange mode as specified in the `psk_key_exchange_modes` extension. Usually 0 or 1. + comp_algs := joinInts(parsed.CertCompressionAlgorithms, "-") // Comma separated list of the certificate compression algorithms as sent in the `compress_certificate` extension + groups := strings.Join(j.PeetPrintCurves, "-") // Comma separated list of supported elliptic curve groups as sent in the `supported_groups` extension. + suites := strings.Join(j.PeetPrintCiphers, "-") // Cipher suites + extensions := strings.Join(j.PeetPrintExtensions, "-") // Extensions // if debug { // fmt.Println("tls_versions:", tls_versions) // fmt.Println("protos:", protos) - // fmt.Println("signature algs:", sig_als) + // fmt.Println("signature algs:", sig_algs) // fmt.Println("key_mode:", key_mode) // fmt.Println("comp_algs:", comp_algs) // fmt.Println("groups:", groups) @@ -190,6 +190,6 @@ func CalculatePeetPrint(parsed ClientHello, j JA3Calculating) (string, string) { // fmt.Println("extensions:", extensions) // } - fp := fmt.Sprintf("%v|%v|%v|%v|%v|%v|%v|%v", tls_versions, protos, groups, sig_als, key_mode, comp_algs, suites, extensions) + fp := fmt.Sprintf("%v|%v|%v|%v|%v|%v|%v|%v", tls_versions, protos, groups, sig_algs, key_mode, comp_algs, suites, extensions) return fp, utils.GetMD5Hash(fp) } diff --git a/pkg/tls/ja4.go b/pkg/tls/ja4.go index dded4ec..338673a 100644 --- a/pkg/tls/ja4.go +++ b/pkg/tls/ja4.go @@ -32,7 +32,7 @@ func ja4aWithProto(tls *types.TLSDetails, proto string) string { "3": "h3", // HTTP/3 } - tlsVersion := getOrReturnOG(tls.NegotiatedVesion, tlsVersionMapping) + tlsVersion := getOrReturnOG(tls.NegotiatedVersion, tlsVersionMapping) sniMode := "d" // IP: i, domain: d numSuites := len(strings.Split(strings.Split(tls.JA3, ",")[1], "-")) diff --git a/pkg/types/structs.go b/pkg/types/structs.go index 3d6f8c7..6f8430c 100755 --- a/pkg/types/structs.go +++ b/pkg/types/structs.go @@ -8,10 +8,10 @@ import ( ) type TLSDetails struct { - Ciphers []string `json:"ciphers"` - Extensions []interface{} `json:"extensions"` - RecordVersion string `json:"tls_version_record"` - NegotiatedVesion string `json:"tls_version_negotiated"` + Ciphers []string `json:"ciphers"` + Extensions []interface{} `json:"extensions"` + RecordVersion string `json:"tls_version_record"` + NegotiatedVersion string `json:"tls_version_negotiated"` JA3 string `json:"ja3"` JA3Hash string `json:"ja3_hash"`