ja4plus 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. ja4plus-0.2.0/LICENSE +28 -0
  2. ja4plus-0.2.0/MANIFEST.in +3 -0
  3. ja4plus-0.2.0/PKG-INFO +178 -0
  4. ja4plus-0.2.0/README.md +140 -0
  5. ja4plus-0.2.0/ja4plus/__init__.py +30 -0
  6. ja4plus-0.2.0/ja4plus/collector.py +194 -0
  7. ja4plus-0.2.0/ja4plus/fingerprinters/__init__.py +23 -0
  8. ja4plus-0.2.0/ja4plus/fingerprinters/base.py +44 -0
  9. ja4plus-0.2.0/ja4plus/fingerprinters/ja4.py +284 -0
  10. ja4plus-0.2.0/ja4plus/fingerprinters/ja4h.py +131 -0
  11. ja4plus-0.2.0/ja4plus/fingerprinters/ja4l.py +265 -0
  12. ja4plus-0.2.0/ja4plus/fingerprinters/ja4s.py +144 -0
  13. ja4plus-0.2.0/ja4plus/fingerprinters/ja4ssh.py +402 -0
  14. ja4plus-0.2.0/ja4plus/fingerprinters/ja4t.py +88 -0
  15. ja4plus-0.2.0/ja4plus/fingerprinters/ja4ts.py +90 -0
  16. ja4plus-0.2.0/ja4plus/fingerprinters/ja4x.py +300 -0
  17. ja4plus-0.2.0/ja4plus/utils/__init__.py +3 -0
  18. ja4plus-0.2.0/ja4plus/utils/http_utils.py +181 -0
  19. ja4plus-0.2.0/ja4plus/utils/ssh_utils.py +221 -0
  20. ja4plus-0.2.0/ja4plus/utils/tls_utils.py +387 -0
  21. ja4plus-0.2.0/ja4plus/utils/x509_utils.py +352 -0
  22. ja4plus-0.2.0/ja4plus.egg-info/PKG-INFO +178 -0
  23. ja4plus-0.2.0/ja4plus.egg-info/SOURCES.txt +45 -0
  24. ja4plus-0.2.0/ja4plus.egg-info/dependency_links.txt +1 -0
  25. ja4plus-0.2.0/ja4plus.egg-info/requires.txt +6 -0
  26. ja4plus-0.2.0/ja4plus.egg-info/top_level.txt +3 -0
  27. ja4plus-0.2.0/pyproject.toml +48 -0
  28. ja4plus-0.2.0/requirements.txt +7 -0
  29. ja4plus-0.2.0/setup.cfg +4 -0
  30. ja4plus-0.2.0/setup.py +52 -0
  31. ja4plus-0.2.0/tests/test_comprehensive.py +944 -0
  32. ja4plus-0.2.0/tests/test_edge_cases.py +450 -0
  33. ja4plus-0.2.0/tests/test_integration.py +166 -0
  34. ja4plus-0.2.0/tests/test_ja4.py +78 -0
  35. ja4plus-0.2.0/tests/test_ja4_deep.py +448 -0
  36. ja4plus-0.2.0/tests/test_ja4h.py +68 -0
  37. ja4plus-0.2.0/tests/test_ja4h_deep.py +318 -0
  38. ja4plus-0.2.0/tests/test_ja4l.py +107 -0
  39. ja4plus-0.2.0/tests/test_ja4s.py +158 -0
  40. ja4plus-0.2.0/tests/test_ja4ssh.py +180 -0
  41. ja4plus-0.2.0/tests/test_ja4ssh_deep.py +327 -0
  42. ja4plus-0.2.0/tests/test_ja4t.py +97 -0
  43. ja4plus-0.2.0/tests/test_ja4t_deep.py +367 -0
  44. ja4plus-0.2.0/tests/test_ja4ts.py +124 -0
  45. ja4plus-0.2.0/tests/test_ja4x.py +159 -0
  46. ja4plus-0.2.0/tests/test_ja4x_deep.py +326 -0
  47. ja4plus-0.2.0/tests/test_utils.py +505 -0
ja4plus-0.2.0/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, ja4plus contributors
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,3 @@
1
+ include LICENSE
2
+ include README.md
3
+ include requirements.txt
ja4plus-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: ja4plus
3
+ Version: 0.2.0
4
+ Summary: JA4+ network fingerprinting library for TLS, TCP, HTTP, SSH, and X.509 analysis
5
+ Home-page: https://github.com/Crank-Git/ja4plus
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/Crank-Git/ja4plus
8
+ Project-URL: Bug Tracker, https://github.com/Crank-Git/ja4plus/issues
9
+ Project-URL: Source Code, https://github.com/Crank-Git/ja4plus
10
+ Project-URL: Documentation, https://github.com/Crank-Git/ja4plus/tree/main/docs
11
+ Project-URL: JA4+ Specification, https://github.com/FoxIO-LLC/ja4
12
+ Keywords: ja4,ja4plus,fingerprinting,tls,tcp,http,ssh,x509,network,security,scapy
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Information Technology
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: System :: Networking :: Monitoring
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Operating System :: OS Independent
27
+ Requires-Python: >=3.8
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: scapy>=2.4.0
31
+ Requires-Dist: cryptography>=3.4.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=3.0; extra == "dev"
35
+ Dynamic: home-page
36
+ Dynamic: license-file
37
+ Dynamic: requires-python
38
+
39
+ # JA4+
40
+
41
+ A Python library for JA4+ network fingerprinting. Implements all eight JA4+ methods for identifying and classifying network traffic based on TLS, TCP, HTTP, SSH, and X.509 characteristics.
42
+
43
+ JA4+ is a set of network fingerprinting standards created by [FoxIO](https://foxio.io). This library is an independent Python implementation of the published specification. For the original spec, see the [FoxIO JA4+ repository](https://github.com/FoxIO-LLC/ja4).
44
+
45
+ ## Supported Fingerprint Types
46
+
47
+ | Type | Protocol | Description |
48
+ |------|----------|-------------|
49
+ | JA4 | TLS | Client fingerprint from ClientHello messages |
50
+ | JA4S | TLS | Server fingerprint from ServerHello messages |
51
+ | JA4H | HTTP | Client fingerprint from request headers and cookies |
52
+ | JA4T | TCP | Client OS fingerprint from SYN packets |
53
+ | JA4TS | TCP | Server fingerprint from SYN-ACK packets |
54
+ | JA4L | TCP | Light distance and latency estimation |
55
+ | JA4X | X.509 | Certificate structure fingerprint from OID sequences |
56
+ | JA4SSH | SSH | Session type classification from traffic patterns |
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ pip install ja4plus
62
+ ```
63
+
64
+ Or install from source:
65
+
66
+ ```bash
67
+ git clone https://github.com/Crank-Git/ja4plus.git
68
+ cd ja4plus
69
+ pip install -e .
70
+ ```
71
+
72
+ ## Quick Start
73
+
74
+ ```python
75
+ from scapy.all import rdpcap
76
+ from ja4plus import JA4Fingerprinter
77
+
78
+ packets = rdpcap("capture.pcap")
79
+
80
+ fp = JA4Fingerprinter()
81
+ for packet in packets:
82
+ result = fp.process_packet(packet)
83
+ if result:
84
+ print(f"JA4: {result}")
85
+ ```
86
+
87
+ ## Usage
88
+
89
+ ### Class-Based API
90
+
91
+ Each fingerprinter processes packets and collects results:
92
+
93
+ ```python
94
+ from ja4plus import JA4Fingerprinter, JA4SFingerprinter, JA4TFingerprinter
95
+
96
+ ja4 = JA4Fingerprinter()
97
+ ja4s = JA4SFingerprinter()
98
+ ja4t = JA4TFingerprinter()
99
+
100
+ for packet in packets:
101
+ ja4.process_packet(packet)
102
+ ja4s.process_packet(packet)
103
+ ja4t.process_packet(packet)
104
+
105
+ for entry in ja4.get_fingerprints():
106
+ print(entry["fingerprint"])
107
+ ```
108
+
109
+ ### Function-Based API
110
+
111
+ For one-shot fingerprinting of individual packets:
112
+
113
+ ```python
114
+ from ja4plus import generate_ja4, generate_ja4s, generate_ja4h
115
+
116
+ fingerprint = generate_ja4(packet)
117
+ ```
118
+
119
+ ### All Fingerprinters
120
+
121
+ ```python
122
+ from ja4plus import (
123
+ JA4Fingerprinter, # TLS Client
124
+ JA4SFingerprinter, # TLS Server
125
+ JA4HFingerprinter, # HTTP
126
+ JA4TFingerprinter, # TCP Client (SYN)
127
+ JA4TSFingerprinter, # TCP Server (SYN-ACK)
128
+ JA4LFingerprinter, # Latency
129
+ JA4XFingerprinter, # X.509 Certificate
130
+ JA4SSHFingerprinter, # SSH
131
+ )
132
+ ```
133
+
134
+ All fingerprinters share a common interface:
135
+
136
+ | Method | Description |
137
+ |--------|-------------|
138
+ | `process_packet(pkt)` | Process a packet, returns fingerprint string or `None` |
139
+ | `get_fingerprints()` | Returns list of all collected fingerprint dicts |
140
+ | `reset()` | Clears all collected state |
141
+
142
+ See [`docs/usage.md`](docs/usage.md) for detailed usage of each fingerprinter.
143
+
144
+ ## Fingerprint Formats
145
+
146
+ | Type | Format | Example |
147
+ |------|--------|---------|
148
+ | JA4 | `{proto}{ver}{sni}{ciphcnt}{extcnt}{alpn}_{hash}_{hash}` | `t13d1516h2_8daaf6152771_e5627efa2ab1` |
149
+ | JA4S | `{proto}{ver}{extcnt}{alpn}_{cipher}_{hash}` | `t130200_1301_a56c5b993250` |
150
+ | JA4H | `{method}{ver}{cookie}{ref}{cnt}{lang}_{hash}_{hash}_{hash}` | `ge11cr0800_edb4461d7a83_4817af47a558_...` |
151
+ | JA4T | `{window}_{options}_{mss}_{wscale}` | `65535_2-4-8-1-3_1460_7` |
152
+ | JA4TS | `{window}_{options}_{mss}_{wscale}` | `14600_2-4-8-1-3_1460_0` |
153
+ | JA4L | `{latency_us}_{ttl}` | `2500_56` |
154
+ | JA4X | `{issuer_hash}_{subject_hash}_{ext_hash}` | `a37f49ba31e2_a37f49ba31e2_dd4f1a0ef8b2` |
155
+ | JA4SSH | `c{mode}s{mode}_c{pkts}s{pkts}_c{acks}s{acks}` | `c36s36_c51s80_c69s0` |
156
+
157
+ ## Requirements
158
+
159
+ - Python 3.8+
160
+ - [scapy](https://scapy.net/) >= 2.4.0
161
+ - [cryptography](https://cryptography.io/) >= 3.4.0
162
+
163
+ ## Development
164
+
165
+ ```bash
166
+ git clone https://github.com/Crank-Git/ja4plus.git
167
+ cd ja4plus
168
+ pip install -e ".[dev]"
169
+ pytest tests/ -v
170
+ ```
171
+
172
+ ## License
173
+
174
+ BSD 3-Clause License. See [LICENSE](LICENSE) for details.
175
+
176
+ ## Acknowledgments
177
+
178
+ JA4+ was created by John Althouse at [FoxIO](https://foxio.io). This library is an independent implementation of the published specification. For the original spec and reference implementation, see [github.com/FoxIO-LLC/ja4](https://github.com/FoxIO-LLC/ja4).
@@ -0,0 +1,140 @@
1
+ # JA4+
2
+
3
+ A Python library for JA4+ network fingerprinting. Implements all eight JA4+ methods for identifying and classifying network traffic based on TLS, TCP, HTTP, SSH, and X.509 characteristics.
4
+
5
+ JA4+ is a set of network fingerprinting standards created by [FoxIO](https://foxio.io). This library is an independent Python implementation of the published specification. For the original spec, see the [FoxIO JA4+ repository](https://github.com/FoxIO-LLC/ja4).
6
+
7
+ ## Supported Fingerprint Types
8
+
9
+ | Type | Protocol | Description |
10
+ |------|----------|-------------|
11
+ | JA4 | TLS | Client fingerprint from ClientHello messages |
12
+ | JA4S | TLS | Server fingerprint from ServerHello messages |
13
+ | JA4H | HTTP | Client fingerprint from request headers and cookies |
14
+ | JA4T | TCP | Client OS fingerprint from SYN packets |
15
+ | JA4TS | TCP | Server fingerprint from SYN-ACK packets |
16
+ | JA4L | TCP | Light distance and latency estimation |
17
+ | JA4X | X.509 | Certificate structure fingerprint from OID sequences |
18
+ | JA4SSH | SSH | Session type classification from traffic patterns |
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install ja4plus
24
+ ```
25
+
26
+ Or install from source:
27
+
28
+ ```bash
29
+ git clone https://github.com/Crank-Git/ja4plus.git
30
+ cd ja4plus
31
+ pip install -e .
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ from scapy.all import rdpcap
38
+ from ja4plus import JA4Fingerprinter
39
+
40
+ packets = rdpcap("capture.pcap")
41
+
42
+ fp = JA4Fingerprinter()
43
+ for packet in packets:
44
+ result = fp.process_packet(packet)
45
+ if result:
46
+ print(f"JA4: {result}")
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ### Class-Based API
52
+
53
+ Each fingerprinter processes packets and collects results:
54
+
55
+ ```python
56
+ from ja4plus import JA4Fingerprinter, JA4SFingerprinter, JA4TFingerprinter
57
+
58
+ ja4 = JA4Fingerprinter()
59
+ ja4s = JA4SFingerprinter()
60
+ ja4t = JA4TFingerprinter()
61
+
62
+ for packet in packets:
63
+ ja4.process_packet(packet)
64
+ ja4s.process_packet(packet)
65
+ ja4t.process_packet(packet)
66
+
67
+ for entry in ja4.get_fingerprints():
68
+ print(entry["fingerprint"])
69
+ ```
70
+
71
+ ### Function-Based API
72
+
73
+ For one-shot fingerprinting of individual packets:
74
+
75
+ ```python
76
+ from ja4plus import generate_ja4, generate_ja4s, generate_ja4h
77
+
78
+ fingerprint = generate_ja4(packet)
79
+ ```
80
+
81
+ ### All Fingerprinters
82
+
83
+ ```python
84
+ from ja4plus import (
85
+ JA4Fingerprinter, # TLS Client
86
+ JA4SFingerprinter, # TLS Server
87
+ JA4HFingerprinter, # HTTP
88
+ JA4TFingerprinter, # TCP Client (SYN)
89
+ JA4TSFingerprinter, # TCP Server (SYN-ACK)
90
+ JA4LFingerprinter, # Latency
91
+ JA4XFingerprinter, # X.509 Certificate
92
+ JA4SSHFingerprinter, # SSH
93
+ )
94
+ ```
95
+
96
+ All fingerprinters share a common interface:
97
+
98
+ | Method | Description |
99
+ |--------|-------------|
100
+ | `process_packet(pkt)` | Process a packet, returns fingerprint string or `None` |
101
+ | `get_fingerprints()` | Returns list of all collected fingerprint dicts |
102
+ | `reset()` | Clears all collected state |
103
+
104
+ See [`docs/usage.md`](docs/usage.md) for detailed usage of each fingerprinter.
105
+
106
+ ## Fingerprint Formats
107
+
108
+ | Type | Format | Example |
109
+ |------|--------|---------|
110
+ | JA4 | `{proto}{ver}{sni}{ciphcnt}{extcnt}{alpn}_{hash}_{hash}` | `t13d1516h2_8daaf6152771_e5627efa2ab1` |
111
+ | JA4S | `{proto}{ver}{extcnt}{alpn}_{cipher}_{hash}` | `t130200_1301_a56c5b993250` |
112
+ | JA4H | `{method}{ver}{cookie}{ref}{cnt}{lang}_{hash}_{hash}_{hash}` | `ge11cr0800_edb4461d7a83_4817af47a558_...` |
113
+ | JA4T | `{window}_{options}_{mss}_{wscale}` | `65535_2-4-8-1-3_1460_7` |
114
+ | JA4TS | `{window}_{options}_{mss}_{wscale}` | `14600_2-4-8-1-3_1460_0` |
115
+ | JA4L | `{latency_us}_{ttl}` | `2500_56` |
116
+ | JA4X | `{issuer_hash}_{subject_hash}_{ext_hash}` | `a37f49ba31e2_a37f49ba31e2_dd4f1a0ef8b2` |
117
+ | JA4SSH | `c{mode}s{mode}_c{pkts}s{pkts}_c{acks}s{acks}` | `c36s36_c51s80_c69s0` |
118
+
119
+ ## Requirements
120
+
121
+ - Python 3.8+
122
+ - [scapy](https://scapy.net/) >= 2.4.0
123
+ - [cryptography](https://cryptography.io/) >= 3.4.0
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ git clone https://github.com/Crank-Git/ja4plus.git
129
+ cd ja4plus
130
+ pip install -e ".[dev]"
131
+ pytest tests/ -v
132
+ ```
133
+
134
+ ## License
135
+
136
+ BSD 3-Clause License. See [LICENSE](LICENSE) for details.
137
+
138
+ ## Acknowledgments
139
+
140
+ JA4+ was created by John Althouse at [FoxIO](https://foxio.io). This library is an independent implementation of the published specification. For the original spec and reference implementation, see [github.com/FoxIO-LLC/ja4](https://github.com/FoxIO-LLC/ja4).
@@ -0,0 +1,30 @@
1
+ """
2
+ JA4+ - Network Fingerprinting Library
3
+
4
+ A Python implementation of the JA4+ network fingerprinting methods
5
+ created by FoxIO LLC. Supports TLS, TCP, HTTP, SSH, and X.509
6
+ fingerprinting for network security monitoring and traffic analysis.
7
+ """
8
+
9
+ from ja4plus.fingerprinters.ja4 import JA4Fingerprinter
10
+ from ja4plus.fingerprinters.ja4s import JA4SFingerprinter
11
+ from ja4plus.fingerprinters.ja4h import JA4HFingerprinter
12
+ from ja4plus.fingerprinters.ja4l import JA4LFingerprinter
13
+ from ja4plus.fingerprinters.ja4x import JA4XFingerprinter
14
+ from ja4plus.fingerprinters.ja4ssh import JA4SSHFingerprinter
15
+ from ja4plus.fingerprinters.ja4t import JA4TFingerprinter
16
+ from ja4plus.fingerprinters.ja4ts import JA4TSFingerprinter
17
+
18
+ # Function-based API
19
+ from ja4plus.fingerprinters.ja4 import generate_ja4
20
+ from ja4plus.fingerprinters.ja4s import generate_ja4s
21
+ from ja4plus.fingerprinters.ja4h import generate_ja4h
22
+ from ja4plus.fingerprinters.ja4l import generate_ja4l
23
+ from ja4plus.fingerprinters.ja4x import generate_ja4x
24
+ from ja4plus.fingerprinters.ja4ssh import generate_ja4ssh
25
+ from ja4plus.fingerprinters.ja4t import generate_ja4t
26
+ from ja4plus.fingerprinters.ja4ts import generate_ja4ts
27
+
28
+ __version__ = "0.2.0"
29
+ __author__ = "ja4plus contributors"
30
+ __license__ = "BSD-3-Clause"
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ JA4+ collector for Elastic Agent integration.
4
+ Captures network traffic and outputs JA4+ fingerprints as JSON.
5
+ """
6
+
7
+ import argparse
8
+ import json
9
+ import sys
10
+ import time
11
+ import signal
12
+ from scapy.all import sniff
13
+
14
+ from ja4plus.fingerprinters.ja4 import JA4Fingerprinter
15
+ from ja4plus.fingerprinters.ja4s import JA4SFingerprinter
16
+ from ja4plus.fingerprinters.ja4h import JA4HFingerprinter
17
+ from ja4plus.fingerprinters.ja4t import JA4TFingerprinter
18
+ from ja4plus.fingerprinters.ja4ts import JA4TSFingerprinter
19
+ from ja4plus.fingerprinters.ja4x import JA4XFingerprinter
20
+ from ja4plus.fingerprinters.ja4ssh import JA4SSHFingerprinter
21
+
22
+ # Global variables for signal handling
23
+ packet_count = 0
24
+ max_packets = 0
25
+ start_time = 0
26
+ timeout_seconds = 0
27
+
28
+ def signal_handler(signum, frame):
29
+ """Handle termination signals gracefully"""
30
+ sys.stderr.write("Received signal, shutting down gracefully...\n")
31
+ sys.exit(0)
32
+
33
+ def main():
34
+ """Main collector function"""
35
+ global packet_count, max_packets, start_time, timeout_seconds
36
+
37
+ parser = argparse.ArgumentParser(description="JA4+ Network Fingerprinting Collector")
38
+ parser.add_argument("--interface", "-i", default="any", help="Network interface to capture on")
39
+ parser.add_argument("--filter", "-f", default="tcp or udp", help="BPF filter expression")
40
+ parser.add_argument("--fingerprinters", default="ja4,ja4s,ja4h,ja4t,ja4ts,ja4x,ja4ssh",
41
+ help="Comma-separated list of fingerprinters to enable")
42
+ parser.add_argument("--max-packets", type=int, default=0,
43
+ help="Maximum packets to process (0 = unlimited)")
44
+ parser.add_argument("--timeout", type=float, default=0,
45
+ help="Timeout in seconds (0 = no timeout)")
46
+ parser.add_argument("--output", choices=["json", "text"], default="json",
47
+ help="Output format")
48
+
49
+ args = parser.parse_args()
50
+
51
+ # Set up signal handlers
52
+ signal.signal(signal.SIGINT, signal_handler)
53
+ signal.signal(signal.SIGTERM, signal_handler)
54
+
55
+ # Initialize global variables
56
+ max_packets = args.max_packets
57
+ timeout_seconds = args.timeout
58
+ start_time = time.time()
59
+
60
+ # Initialize enabled fingerprinters
61
+ available_fingerprinters = {
62
+ "ja4": JA4Fingerprinter(),
63
+ "ja4s": JA4SFingerprinter(),
64
+ "ja4h": JA4HFingerprinter(),
65
+ "ja4t": JA4TFingerprinter(),
66
+ "ja4ts": JA4TSFingerprinter(),
67
+ "ja4x": JA4XFingerprinter(),
68
+ "ja4ssh": JA4SSHFingerprinter()
69
+ }
70
+
71
+ enabled_fps = args.fingerprinters.split(",")
72
+ fingerprinters = {}
73
+ for name in enabled_fps:
74
+ name = name.strip()
75
+ if name in available_fingerprinters:
76
+ fingerprinters[name] = available_fingerprinters[name]
77
+ else:
78
+ sys.stderr.write(f"Warning: Unknown fingerprinter '{name}'\n")
79
+
80
+ if not fingerprinters:
81
+ sys.stderr.write("Error: No valid fingerprinters enabled\n")
82
+ sys.exit(1)
83
+
84
+ def process_packet(packet):
85
+ """Process packet with enabled fingerprinters and output results"""
86
+ global packet_count
87
+
88
+ # Check packet limit
89
+ if max_packets > 0 and packet_count >= max_packets:
90
+ return
91
+
92
+ # Check timeout
93
+ if timeout_seconds > 0 and (time.time() - start_time) >= timeout_seconds:
94
+ return
95
+
96
+ packet_count += 1
97
+
98
+ results = {
99
+ "@timestamp": int(time.time() * 1000), # Elasticsearch expects milliseconds
100
+ "ja4plus": {}
101
+ }
102
+
103
+ # Apply each enabled fingerprinter
104
+ for name, fp in fingerprinters.items():
105
+ try:
106
+ fingerprint = fp.process_packet(packet)
107
+ if fingerprint:
108
+ results["ja4plus"][name] = fingerprint
109
+ except Exception as e:
110
+ sys.stderr.write(f"Error in {name} fingerprinter: {e}\n")
111
+
112
+ # Only output if we have fingerprints
113
+ if results["ja4plus"]:
114
+ # Add network metadata - properly extract IP addresses
115
+ try:
116
+ from scapy.all import IP, IPv6
117
+
118
+ # Look for IP layer (IPv4 or IPv6)
119
+ if packet.haslayer(IP):
120
+ ip_layer = packet[IP]
121
+ results["source"] = {"ip": ip_layer.src}
122
+ results["destination"] = {"ip": ip_layer.dst}
123
+ elif packet.haslayer(IPv6):
124
+ ip_layer = packet[IPv6]
125
+ results["source"] = {"ip": ip_layer.src}
126
+ results["destination"] = {"ip": ip_layer.dst}
127
+ elif hasattr(packet, "src") and hasattr(packet, "dst"):
128
+ # Fallback to packet-level src/dst (might be MAC addresses)
129
+ # Only use if they look like IP addresses
130
+ src_str = str(packet.src)
131
+ dst_str = str(packet.dst)
132
+ if ":" not in src_str or len(src_str.split(":")) <= 4: # Not a MAC address
133
+ results["source"] = {"ip": src_str}
134
+ results["destination"] = {"ip": dst_str}
135
+
136
+ # Add port information if available
137
+ from scapy.all import TCP, UDP
138
+ if packet.haslayer(TCP):
139
+ tcp_layer = packet[TCP]
140
+ if "source" in results:
141
+ results["source"]["port"] = tcp_layer.sport
142
+ if "destination" in results:
143
+ results["destination"]["port"] = tcp_layer.dport
144
+ elif packet.haslayer(UDP):
145
+ udp_layer = packet[UDP]
146
+ if "source" in results:
147
+ results["source"]["port"] = udp_layer.sport
148
+ if "destination" in results:
149
+ results["destination"]["port"] = udp_layer.dport
150
+
151
+ except Exception as e:
152
+ sys.stderr.write(f"Error extracting network metadata: {e}\n")
153
+
154
+ # Add collection metadata
155
+ results["ja4plus"]["interface"] = args.interface
156
+ results["ja4plus"]["filter"] = args.filter
157
+ results["ja4plus"]["collection_timestamp"] = int(time.time() * 1000)
158
+
159
+ # Output based on format
160
+ if args.output == "json":
161
+ print(json.dumps(results))
162
+ else:
163
+ print(f"Packet {packet_count}: {results}")
164
+
165
+ sys.stdout.flush()
166
+
167
+ # Stop if we've reached the packet limit
168
+ if max_packets > 0 and packet_count >= max_packets:
169
+ sys.stderr.write(f"Reached packet limit of {max_packets}, stopping...\n")
170
+ sys.exit(0)
171
+
172
+ # Stop if we've reached the timeout
173
+ if timeout_seconds > 0 and (time.time() - start_time) >= timeout_seconds:
174
+ sys.stderr.write(f"Reached timeout of {timeout_seconds} seconds, stopping...\n")
175
+ sys.exit(0)
176
+
177
+ try:
178
+ sys.stderr.write(f"Starting JA4+ collection on interface '{args.interface}' with filter '{args.filter}'\n")
179
+ sys.stderr.write(f"Enabled fingerprinters: {', '.join(fingerprinters.keys())}\n")
180
+
181
+ # Start sniffing
182
+ sniff(prn=process_packet,
183
+ filter=args.filter,
184
+ iface=args.interface if args.interface != "any" else None,
185
+ store=0)
186
+
187
+ except KeyboardInterrupt:
188
+ sys.stderr.write("Interrupted by user\n")
189
+ except Exception as e:
190
+ sys.stderr.write(f"Error during packet capture: {e}\n")
191
+ sys.exit(1)
192
+
193
+ if __name__ == "__main__":
194
+ main()
@@ -0,0 +1,23 @@
1
+ """
2
+ JA4+ fingerprinters for various protocols and traffic types.
3
+ """
4
+
5
+ from .ja4 import JA4Fingerprinter
6
+ from .ja4s import JA4SFingerprinter
7
+ from .ja4h import JA4HFingerprinter
8
+ from .ja4l import JA4LFingerprinter
9
+ from .ja4x import JA4XFingerprinter
10
+ from .ja4ssh import JA4SSHFingerprinter
11
+ from .ja4t import JA4TFingerprinter
12
+ from .ja4ts import JA4TSFingerprinter
13
+
14
+ __all__ = [
15
+ 'JA4Fingerprinter',
16
+ 'JA4SFingerprinter',
17
+ 'JA4HFingerprinter',
18
+ 'JA4LFingerprinter',
19
+ 'JA4XFingerprinter',
20
+ 'JA4SSHFingerprinter',
21
+ 'JA4TFingerprinter',
22
+ 'JA4TSFingerprinter'
23
+ ]
@@ -0,0 +1,44 @@
1
+ """
2
+ Base fingerprinter class for JA4+ fingerprinters.
3
+ """
4
+
5
+ class BaseFingerprinter:
6
+ """Base class for all JA4+ fingerprinters."""
7
+
8
+ def __init__(self):
9
+ """Initialize the fingerprinter."""
10
+ self.fingerprints = []
11
+
12
+ def process_packet(self, packet):
13
+ """
14
+ Process a packet and extract fingerprint if applicable.
15
+ This method should be overridden by subclasses.
16
+
17
+ Args:
18
+ packet: A network packet to analyze
19
+
20
+ Returns:
21
+ The extracted fingerprint if successful, None otherwise
22
+ """
23
+ raise NotImplementedError("Subclasses must implement this method.")
24
+
25
+ def add_fingerprint(self, fingerprint, packet):
26
+ """
27
+ Add a fingerprint to the collection.
28
+
29
+ Args:
30
+ fingerprint: The extracted fingerprint
31
+ packet: The packet that generated this fingerprint
32
+ """
33
+ self.fingerprints.append({
34
+ 'fingerprint': fingerprint,
35
+ 'packet': packet
36
+ })
37
+
38
+ def get_fingerprints(self):
39
+ """Return all collected fingerprints."""
40
+ return self.fingerprints
41
+
42
+ def reset(self):
43
+ """Reset the fingerprinter state."""
44
+ self.fingerprints = []