diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index ff472caf1..5d729e323 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -193,7 +193,8 @@ "Protobuf Decode", "VarInt Encode", "VarInt Decode", - "TLS JA3 Fingerprint", + "JA3 Fingerprint", + "JA3S Fingerprint", "Format MAC addresses", "Change IP format", "Group IP addresses", diff --git a/src/core/operations/TLSJA3Fingerprint.mjs b/src/core/operations/JA3Fingerprint.mjs similarity index 96% rename from src/core/operations/TLSJA3Fingerprint.mjs rename to src/core/operations/JA3Fingerprint.mjs index d906d032d..0384c6e41 100644 --- a/src/core/operations/TLSJA3Fingerprint.mjs +++ b/src/core/operations/JA3Fingerprint.mjs @@ -18,17 +18,17 @@ import Stream from "../lib/Stream.mjs"; import {runHash} from "../lib/Hash.mjs"; /** - * TLS JA3 Fingerprint operation + * JA3 Fingerprint operation */ -class TLSJA3Fingerprint extends Operation { +class JA3Fingerprint extends Operation { /** - * TLSJA3Fingerprint constructor + * JA3Fingerprint constructor */ constructor() { super(); - this.name = "TLS JA3 Fingerprint"; + this.name = "JA3 Fingerprint"; this.module = "Crypto"; this.description = "Generates a JA3 fingerprint to help identify TLS clients based on hashing together values from the Client Hello.

Input: A hex stream of the TLS Client Hello application layer."; this.infoURL = "https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967"; @@ -202,4 +202,4 @@ const GREASE_CIPHERSUITES = [ 0xfafa ]; -export default TLSJA3Fingerprint; +export default JA3Fingerprint; diff --git a/src/core/operations/JA3SFingerprint.mjs b/src/core/operations/JA3SFingerprint.mjs new file mode 100644 index 000000000..b65705013 --- /dev/null +++ b/src/core/operations/JA3SFingerprint.mjs @@ -0,0 +1,145 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2021 + * @license Apache-2.0 + * + * JA3S created by Salesforce + * John B. Althouse + * Jeff Atkinson + * Josh Atkins + * + * Algorithm released under the BSD-3-clause licence + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import Stream from "../lib/Stream.mjs"; +import {runHash} from "../lib/Hash.mjs"; + +/** + * JA3S Fingerprint operation + */ +class JA3SFingerprint extends Operation { + + /** + * JA3SFingerprint constructor + */ + constructor() { + super(); + + this.name = "JA3S Fingerprint"; + this.module = "Crypto"; + this.description = "Generates a JA3S fingerprint to help identify TLS servers based on hashing together values from the Server Hello.

Input: A hex stream of the TLS Server Hello record in the application layer."; + this.infoURL = "https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Input format", + type: "option", + value: ["Hex", "Base64", "Raw"] + }, + { + name: "Output format", + type: "option", + value: ["Hash digest", "JA3S string", "Full details"] + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [inputFormat, outputFormat] = args; + + input = Utils.convertToByteArray(input, inputFormat); + const s = new Stream(new Uint8Array(input)); + + const handshake = s.readInt(1); + if (handshake !== 0x16) + throw new OperationError("Not handshake data."); + + // Version + s.moveForwardsBy(2); + + // Length + const length = s.readInt(2); + if (s.length !== length + 5) + throw new OperationError("Incorrect handshake length."); + + // Handshake type + const handshakeType = s.readInt(1); + if (handshakeType !== 2) + throw new OperationError("Not a Server Hello."); + + // Handshake length + const handshakeLength = s.readInt(3); + if (s.length !== handshakeLength + 9) + throw new OperationError("Not enough data in Server Hello."); + + // Hello version + const helloVersion = s.readInt(2); + + // Random + s.moveForwardsBy(32); + + // Session ID + const sessionIDLength = s.readInt(1); + s.moveForwardsBy(sessionIDLength); + + // Cipher suite + const cipherSuite = s.readInt(2); + + // Compression Method + s.moveForwardsBy(1); + + // Extensions + const extensionsLength = s.readInt(2); + const extensions = s.getBytes(extensionsLength); + const es = new Stream(extensions); + const exts = []; + while (es.hasMore()) { + const type = es.readInt(2); + const length = es.readInt(2); + es.moveForwardsBy(length); + exts.push(type); + } + + // Output + const ja3s = [ + helloVersion.toString(), + cipherSuite, + exts.join("-") + ]; + const ja3sStr = ja3s.join(","); + const ja3sHash = runHash("md5", Utils.strToArrayBuffer(ja3sStr)); + + switch (outputFormat) { + case "JA3S string": + return ja3sStr; + case "Full details": + return `Hash digest: +${ja3sHash} + +Full JA3S string: +${ja3sStr} + +TLS Version: +${helloVersion.toString()} +Cipher Suite: +${cipherSuite} +Extensions: +${exts.join("-")}`; + case "Hash digest": + default: + return ja3sHash; + } + } + +} + +export default JA3SFingerprint; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index fdce51317..8694c4432 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -104,7 +104,7 @@ import "./tests/Unicode.mjs"; import "./tests/RSA.mjs"; import "./tests/CBOREncode.mjs"; import "./tests/CBORDecode.mjs"; -import "./tests/TLSJA3Fingerprint.mjs"; +import "./tests/JA3Fingerprint.mjs"; // Cannot test operations that use the File type yet diff --git a/tests/operations/tests/TLSJA3Fingerprint.mjs b/tests/operations/tests/JA3Fingerprint.mjs similarity index 89% rename from tests/operations/tests/TLSJA3Fingerprint.mjs rename to tests/operations/tests/JA3Fingerprint.mjs index 4f1534fb9..618dc73d1 100644 --- a/tests/operations/tests/TLSJA3Fingerprint.mjs +++ b/tests/operations/tests/JA3Fingerprint.mjs @@ -1,5 +1,5 @@ /** - * TLSJA3Fingerprint tests. + * JA3Fingerprint tests. * * @author n1474335 [n1474335@gmail.com] * @copyright Crown Copyright 2021 @@ -9,45 +9,45 @@ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { - name: "TLS JA3 Fingerprint: TLS 1.0", + name: "JA3 Fingerprint: TLS 1.0", input: "16030100a4010000a00301543dd2dd48f517ca9a93b1e599f019fdece704a23e86c1dcac588427abbaddf200005cc014c00a0039003800880087c00fc00500350084c012c00800160013c00dc003000ac013c00900330032009a009900450044c00ec004002f009600410007c011c007c00cc002000500040015001200090014001100080006000300ff0100001b000b000403000102000a000600040018001700230000000f000101", expectedOutput: "503053a0c5b2bd9b9334bf7f3d3b8852", recipeConfig: [ { - "op": "TLS JA3 Fingerprint", + "op": "JA3 Fingerprint", "args": ["Hex", "Hash digest"] } ], }, { - name: "TLS JA3 Fingerprint: TLS 1.1", + name: "JA3 Fingerprint: TLS 1.1", input: "16030100a4010000a00302543dd2ed907e47d0086f34bee2c52dd6ccd8de63ba9387f5e810b09d9d49b38000005cc014c00a0039003800880087c00fc00500350084c012c00800160013c00dc003000ac013c00900330032009a009900450044c00ec004002f009600410007c011c007c00cc002000500040015001200090014001100080006000300ff0100001b000b000403000102000a000600040018001700230000000f000101", expectedOutput: "a314eb64cee6cb832aaaa372c8295bab", recipeConfig: [ { - "op": "TLS JA3 Fingerprint", + "op": "JA3 Fingerprint", "args": ["Hex", "Hash digest"] } ], }, { - name: "TLS JA3 Fingerprint: TLS 1.2", + name: "JA3 Fingerprint: TLS 1.2", input: "1603010102010000fe0303543dd3283283692d85f9416b5ccc65d2aafca45c6530b3c6eafbf6d371b6a015000094c030c02cc028c024c014c00a00a3009f006b006a0039003800880087c032c02ec02ac026c00fc005009d003d00350084c012c00800160013c00dc003000ac02fc02bc027c023c013c00900a2009e0067004000330032009a009900450044c031c02dc029c025c00ec004009c003c002f009600410007c011c007c00cc002000500040015001200090014001100080006000300ff01000041000b000403000102000a000600040018001700230000000d002200200601060206030501050205030401040204030301030203030201020202030101000f000101", expectedOutput: "c1a36e1a870786cc75edddc0009eaf3a", recipeConfig: [ { - "op": "TLS JA3 Fingerprint", + "op": "JA3 Fingerprint", "args": ["Hex", "Hash digest"] } ], }, { - name: "TLS JA3 Fingerprint: TLS 1.3", + name: "JA3 Fingerprint: TLS 1.3", input: "1603010200010001fc03034355d402c132771a9386b6e9994ae37069e0621af504c26673b1343843c21d8d0000264a4a130113021303c02bc02fc02cc030cca9cca8cc14cc13c013c014009c009d002f0035000a010001addada0000ff01000100000000180016000013626c6f672e636c6f7564666c6172652e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b000201000028002b00295a5a000100001d0020cf78b9167af054b922a96752b43973107b2a57766357dd288b2b42ab5df30e08002d00020101002b000b0acaca7f12030303020301000a000a00085a5a001d001700180a0a000100001500e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", expectedOutput: "4826a90ec2daf4f7b4b64cc1c8bd343b", recipeConfig: [ { - "op": "TLS JA3 Fingerprint", + "op": "JA3 Fingerprint", "args": ["Hex", "Hash digest"] } ], diff --git a/tests/operations/tests/JA3SFingerprint.mjs b/tests/operations/tests/JA3SFingerprint.mjs new file mode 100644 index 000000000..047018e86 --- /dev/null +++ b/tests/operations/tests/JA3SFingerprint.mjs @@ -0,0 +1,55 @@ +/** + * JA3SFingerprint tests. + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2021 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "JA3S Fingerprint: TLS 1.0", + input: "160301003d020000390301543dd2ddedbfe33895bd6bc676a3fa6b9fe5773a6e04d5476d1af3bcbc1dcbbb00c011000011ff01000100000b00040300010200230000", + expectedOutput: "bed95e1b525d2f41db3a6d68fac5b566", + recipeConfig: [ + { + "op": "JA3S Fingerprint", + "args": ["Hex", "Hash digest"] + } + ], + }, + { + name: "JA3S Fingerprint: TLS 1.1", + input: "160302003d020000390302543dd2ed88131999a0120d36c14a4139671d75aae3d7d7779081d3cf7dd7725a00c013000011ff01000100000b00040300010200230000", + expectedOutput: "130fac2dc19b142500acb0abc63b6379", + recipeConfig: [ + { + "op": "JA3S Fingerprint", + "args": ["Hex", "Hash digest"] + } + ], + }, + { + name: "JA3S Fingerprint: TLS 1.2", + input: "160303003d020000390303543dd328b38b445686739d58fab733fa23838f575e0e5ad9a1b9baace6cc3b4100c02f000011ff01000100000b00040300010200230000", + expectedOutput: "ccc514751b175866924439bdbb5bba34", + recipeConfig: [ + { + "op": "JA3S Fingerprint", + "args": ["Hex", "Hash digest"] + } + ], + }, + { + name: "JA3S Fingerprint: TLS 1.3", + input: "16030100520200004e7f123ef1609fd3f4fa8668aac5822d500fb0639b22671d0fb7258597355795511bf61301002800280024001d0020ae0e282a3b7a463e71064ecbaf671586e979b0edbebf7a4735c31678c70f660c", + expectedOutput: "986ae432c402479fe7a0c6fbe02164c1", + recipeConfig: [ + { + "op": "JA3S Fingerprint", + "args": ["Hex", "Hash digest"] + } + ], + }, +]);