Skip to content

Commit cee9520

Browse files
Quite a bit of overhauling things
1 parent e4efbb3 commit cee9520

25 files changed

+2314
-188
lines changed
Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,39 @@
1-
name: golangci-lint
1+
name: Checks
2+
23
on:
34
push:
5+
branches: [ "main" ]
46
pull_request:
5-
permissions:
6-
contents: read
7-
# Optional: allow read access to pull request. Use with `only-new-issues` option.
8-
# pull-requests: read
7+
branches: [ "main" ]
8+
99
jobs:
10-
golangci:
11-
name: lint
10+
11+
build:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/setup-go@v3
15-
with:
16-
go-version: '1.20'
17-
- uses: actions/checkout@v3
18-
- name: golangci-lint
19-
uses: golangci/golangci-lint-action@v3
20-
with:
21-
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
22-
version: v1.29
23-
24-
# Optional: working directory, useful for monorepos
25-
# working-directory: somedir
26-
27-
# Optional: golangci-lint command line arguments.
28-
# args: --issues-exit-code=0
29-
30-
# Optional: show only new issues if it's a pull request. The default value is `false`.
31-
# only-new-issues: true
32-
33-
# Optional: if set to true then the all caching functionality will be complete disabled,
34-
# takes precedence over all other caching options.
35-
# skip-cache: true
36-
37-
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
38-
# skip-pkg-cache: true
39-
40-
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
41-
# skip-build-cache: true
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up Go
17+
uses: actions/setup-go@v4
18+
with:
19+
go-version: '1.23'
20+
check-latest: true
21+
cache-dependency-path: "**/*.sum"
22+
23+
- name: Install dependencies
24+
run: go mod download
25+
26+
- name: Unit tests
27+
run: |
28+
go test -v ./...
29+
30+
- name: Gosec Security Scanner
31+
uses: securego/gosec@master
32+
with:
33+
args: ./...
34+
35+
- name: Golangci-lint
36+
uses: golangci/golangci-lint-action@v6.1.1
37+
38+
- name: TruffleHog OSS
39+
uses: trufflesecurity/trufflehog@v3.84.2

constants.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dactyloscopy
2+
3+
const (
4+
// Configuration Constants
5+
minPacketLength = 47
6+
7+
// TLS Extension types
8+
ExtServerName uint16 = 0x0000
9+
ExtEllipticCurves uint16 = 0x000a
10+
ExtECPointFormats uint16 = 0x000b
11+
ExtSignatureAlgorithms uint16 = 0x000d
12+
ExtALPN uint16 = 0x0010
13+
ExtSupportedVersions uint16 = 0x002b
14+
ExtPadding uint16 = 0x0015
15+
16+
// TLS Protocol Versions
17+
VersionTLS10 uint16 = 0x0301
18+
VersionTLS11 uint16 = 0x0302
19+
VersionTLS12 uint16 = 0x0303
20+
VersionTLS13 uint16 = 0x0304
21+
)
22+
23+
// Common GREASE values used by clients
24+
var GreaseValues = []uint16{
25+
0x0A0A, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A,
26+
0x5A5A, 0x6A6A, 0x7A7A, 0x8A8A, 0x9A9A,
27+
0xAAAA, 0xBABA, 0xCACA, 0xDADA, 0xEAEA,
28+
0xFAFA,
29+
}

example/example

5.14 MB
Binary file not shown.

example/example.go

Lines changed: 123 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ import (
2222
"encoding/json"
2323
"flag"
2424
"fmt"
25+
"log"
2526
"os"
2627

2728
"github.com/LeeBrotherston/dactyloscopy"
2829
"github.com/google/gopacket"
30+
"github.com/google/gopacket/ip4defrag"
31+
"github.com/google/gopacket/layers"
2932
"github.com/google/gopacket/pcap"
33+
"github.com/google/gopacket/tcpassembly"
34+
"github.com/google/gopacket/tcpassembly/tcpreader"
3035
)
3136

3237
func doSniff(device string, file string) error {
@@ -54,37 +59,141 @@ func doSniff(device string, file string) error {
5459
return fmt.Errorf("need a file or interface")
5560
}
5661
// Yes yes, I know... But offsetting this to the kernel *drastically* reduces processing time
57-
err = handle.SetBPFFilter("(tcp[tcp[12]/16*4]=22 and (tcp[tcp[12]/16*4+5]=1) and (tcp[tcp[12]/16*4+9]=3) and (tcp[tcp[12]/16*4+1]=3)) or (ip6[(ip6[52]/16*4)+40]=22 and (ip6[(ip6[52]/16*4+5)+40]=1) and (ip6[(ip6[52]/16*4+9)+40]=3) and (ip6[(ip6[52]/16*4+1)+40]=3)) or ((udp[14] = 6 and udp[16] = 32 and udp[17] = 1) and ((udp[(udp[60]/16*4)+48]=22) and (udp[(udp[60]/16*4)+53]=1) and (udp[(udp[60]/16*4)+57]=3) and (udp[(udp[60]/16*4)+49]=3))) or (proto 41 and ip[26] = 6 and ip[(ip[72]/16*4)+60]=22 and (ip[(ip[72]/16*4+5)+60]=1) and (ip[(ip[72]/16*4+9)+60]=3) and (ip[(ip[72]/16*4+1)+60]=3))")
58-
if err != nil {
59-
return err
60-
}
62+
//err = handle.SetBPFFilter("((tcp[tcp[12]/16*4]=22 and (tcp[tcp[12]/16*4+5]=1) and (tcp[tcp[12]/16*4+9]=3) and (tcp[tcp[12]/16*4+1]=3)) or (ip6[(ip6[52]/16*4)+40]=22 and (ip6[(ip6[52]/16*4+5)+40]=1) and (ip6[(ip6[52]/16*4+9)+40]=3) and (ip6[(ip6[52]/16*4+1)+40]=3)) or ((udp[14] = 6 and udp[16] = 32 and udp[17] = 1) and ((udp[(udp[60]/16*4)+48]=22) and (udp[(udp[60]/16*4)+53]=1) and (udp[(udp[60]/16*4)+57]=3) and (udp[(udp[60]/16*4)+49]=3))) or (proto 41 and ip[26] = 6 and ip[(ip[72]/16*4)+60]=22 and (ip[(ip[72]/16*4+5)+60]=1) and (ip[(ip[72]/16*4+9)+60]=3) and (ip[(ip[72]/16*4+1)+60]=3))) or (ip[6:2] & 0x1fff != 0)")
63+
//if err != nil {
64+
// return err
65+
//}
6166
defer handle.Close()
6267

68+
ip4defragger := ip4defrag.NewIPv4Defragmenter()
69+
streamFactory := &tlsStreamFactory{}
70+
streamPool := tcpassembly.NewStreamPool(streamFactory)
71+
assembler := tcpassembly.NewAssembler(streamPool)
72+
6373
// Use the handle as a packet source to process all packets
6474
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
65-
for packet := range packetSource.Packets() {
6675

67-
var clientHello dactyloscopy.Fingerprint
68-
payload := packet.ApplicationLayer()
76+
for packet := range packetSource.Packets() {
77+
// IP defragmentation
78+
if net4 := packet.NetworkLayer(); net4 != nil {
79+
if ipv4, ok := net4.(*layers.IPv4); ok {
80+
newIPv4, err := ip4defragger.DefragIPv4(ipv4)
81+
if err != nil {
82+
log.Printf("IPv4 defrag error: %v", err)
83+
continue
84+
}
85+
if newIPv4 == nil {
86+
continue // waiting for more fragments
87+
}
88+
//packet = packet
89+
}
90+
}
91+
// TCP reassembly
92+
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
93+
tcp := tcpLayer.(*layers.TCP)
94+
assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
95+
continue
96+
}
97+
// UDP and others: process as before
98+
if incompletePacket(packet) {
99+
continue
100+
}
69101

70-
err = clientHello.ProcessClientHello(payload.Payload())
71-
if err != nil {
72-
fmt.Printf("Error: %v\n", err)
102+
if len(packet.ApplicationLayer().Payload()) < 50 {
103+
continue
73104
}
74105

75-
output, err := json.Marshal(clientHello)
76-
if err != nil {
77-
return err
106+
if err = dactyloscopy.IsClientHello(packet.ApplicationLayer().Payload()); err == nil {
107+
var clientHello dactyloscopy.Fingerprint
108+
err = clientHello.ProcessClientHello(packet.ApplicationLayer().Payload())
109+
if err != nil {
110+
fmt.Printf("Error: %v\n", err)
111+
}
112+
113+
output, err := json.Marshal(clientHello)
114+
if err != nil {
115+
return err
116+
}
117+
fmt.Printf("%s\n", output)
78118
}
79-
fmt.Printf("%s\n", output)
80119
}
81120
return nil
82121
}
83122

123+
func incompletePacket(packet gopacket.Packet) bool {
124+
netLayer := packet.NetworkLayer()
125+
if netLayer == nil {
126+
//log.Printf("missing network layer")
127+
return true
128+
}
129+
if len(netLayer.LayerPayload()) == 0 {
130+
//log.Printf("empty network layer")
131+
return true
132+
}
133+
134+
transLayer := packet.TransportLayer()
135+
if transLayer == nil {
136+
//log.Printf("missing transport layer")
137+
return true
138+
}
139+
if len(transLayer.LayerPayload()) == 0 {
140+
//log.Printf("empty transport layer")
141+
return true
142+
}
143+
144+
appLayer := packet.ApplicationLayer()
145+
if appLayer == nil {
146+
//log.Printf("missing application layer")
147+
return true
148+
}
149+
150+
payloadLen := uint16(len(appLayer.Payload()))
151+
if payloadLen < 60 {
152+
//log.Printf("insufficient payload")
153+
return true
154+
}
155+
156+
if v4, ok := netLayer.(*layers.IPv4); ok {
157+
if v4.Length > payloadLen {
158+
//log.Printf("length mismatch: %d %d", v4.Length, payloadLen)
159+
return true
160+
}
161+
}
162+
return false
163+
}
164+
84165
func main() {
85166
intStr := flag.String("i", "en0", "interface to sniff")
86167
file := flag.String("f", "", "pcap file")
87168
flag.Parse()
88169

89170
doSniff(*intStr, *file)
90171
}
172+
173+
// TLS stream factory and stream for TCP reassembly
174+
175+
type tlsStreamFactory struct{}
176+
177+
func (f *tlsStreamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
178+
r := tcpreader.NewReaderStream()
179+
go processTLSStream(&r)
180+
return &r
181+
}
182+
183+
func processTLSStream(r *tcpreader.ReaderStream) {
184+
buf := make([]byte, 4096)
185+
for {
186+
n, err := r.Read(buf)
187+
if err != nil {
188+
return
189+
}
190+
if n > 0 {
191+
var clientHello dactyloscopy.Fingerprint
192+
err = clientHello.ProcessClientHello(buf[:n])
193+
if err == nil {
194+
output, _ := json.Marshal(clientHello)
195+
fmt.Printf("%s\n", output)
196+
}
197+
}
198+
}
199+
}

example/fingerprints.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
type fingerprint struct {
4+
JA3Digest string
5+
LB1Digest string
6+
Name string
7+
}
8+
9+
var static_fingerprints = []fingerprint{{
10+
JA3Digest: "579ccef312d18482fc42e2b822ca2430",
11+
LB1Digest: "5d75e7f9e50ed137cd48d5ea5e9ebe36",
12+
Name: "Firefox 115.0.2",
13+
}, {
14+
JA3Digest: "366d007990af3b100f90c88fcff57a8a",
15+
LB1Digest: "7a8af08e01539e5863f202db34e5a727",
16+
Name: "Zoom",
17+
}}
18+
19+
// InitFPDB creates fingerprint DB structures, currently separated into JA3 and LB1 "tables"
20+
func InitFPDB(fplist []fingerprint) (map[string]fingerprint, map[string]fingerprint) {
21+
return initFPDBJA3(fplist), initFPDBLB1(fplist)
22+
}
23+
24+
func initFPDBJA3(fp []fingerprint) map[string]fingerprint {
25+
output := map[string]fingerprint{}
26+
for _, y := range fp {
27+
output[y.JA3Digest] = y
28+
}
29+
return output
30+
}
31+
32+
func initFPDBLB1(fp []fingerprint) map[string]fingerprint {
33+
output := map[string]fingerprint{}
34+
for _, y := range fp {
35+
output[y.LB1Digest] = y
36+
}
37+
return output
38+
}

example/go.mod

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
module example
22

3-
go 1.18
3+
go 1.23.0
4+
5+
toolchain go1.24.3
46

57
replace github.com/LeeBrotherston/dactyloscopy => ./../
68

79
require (
8-
github.com/LeeBrotherston/dactyloscopy v0.0.0-20211004030734-27f81f4ef3d5
10+
github.com/LeeBrotherston/dactyloscopy v0.0.0-20241113013421-e4efbb39c13b
911
github.com/google/gopacket v1.1.19
1012
)
1113

1214
require (
13-
golang.org/x/crypto v0.17.0 // indirect
14-
golang.org/x/sys v0.15.0 // indirect
15+
golang.org/x/crypto v0.38.0 // indirect
16+
golang.org/x/sys v0.33.0 // indirect
1517
)

example/go.sum

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF
22
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
33
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
44
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
5-
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
6-
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
5+
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
6+
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
77
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
88
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
99
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
1010
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
11-
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
11+
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
12+
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
1213
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1314
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1415
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
15-
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
16-
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
16+
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
17+
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
1718
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1819
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
1920
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

0 commit comments

Comments
 (0)