Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,31 @@ 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:

```
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)

Expand All @@ -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
Expand Down Expand Up @@ -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
```

Expand Down
54 changes: 27 additions & 27 deletions pkg/server/connection_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
}

Expand Down
24 changes: 12 additions & 12 deletions pkg/tls/fingerprint_tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -170,26 +170,26 @@ 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)
// fmt.Println("cipher suites:", suites)
// 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)
}
2 changes: 1 addition & 1 deletion pkg/tls/ja4.go
Original file line number Diff line number Diff line change
Expand Up @@ -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], "-"))
Expand Down
8 changes: 4 additions & 4 deletions pkg/types/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down