]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer-ssl: generate JA3S fingerprints
authorMats Klepsland <mats.klepsland@gmail.com>
Thu, 15 Nov 2018 18:49:11 +0000 (19:49 +0100)
committerMats Klepsland <mats.klepsland@gmail.com>
Mon, 20 May 2019 12:30:27 +0000 (14:30 +0200)
Generate JA3S fingerprints based on fields in the ServerHello record.

src/app-layer-ssl.c
src/app-layer-ssl.h
src/detect-tls-ja3-hash.c
src/detect-tls-ja3-string.c
src/output-json-tls.c
src/tests/detect-tls-ja3-hash.c
src/tests/detect-tls-ja3-string.c
src/util-lua-ja3.c

index 96411cd268beb0a14bac72f8caa525d053580616..ae7aa46e90008bb5869104e4be69695e18534ab7 100644 (file)
@@ -668,14 +668,12 @@ static inline int TLSDecodeHSHelloVersion(SSLState *ssl_state,
         ssl_state->curr_connp->version = TLS_VERSION_13_PRE_DRAFT16;
     }
 
-    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
-            ssl_config.enable_ja3 && ssl_state->ja3_str == NULL) {
-
-        ssl_state->ja3_str = Ja3BufferInit();
-        if (ssl_state->ja3_str == NULL)
+    if (ssl_config.enable_ja3 && ssl_state->curr_connp->ja3_str == NULL) {
+        ssl_state->curr_connp->ja3_str = Ja3BufferInit();
+        if (ssl_state->curr_connp->ja3_str == NULL)
             return -1;
 
-        int rc = Ja3BufferAddValue(&ssl_state->ja3_str, version);
+        int rc = Ja3BufferAddValue(&ssl_state->curr_connp->ja3_str, version);
         if (rc != 0)
             return -1;
     }
@@ -761,59 +759,63 @@ static inline int TLSDecodeHSHelloCipherSuites(SSLState *ssl_state,
     if (!(HAS_SPACE(2)))
         goto invalid_length;
 
+    uint16_t cipher_suites_length;
+
     if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
-        /* Skip cipher suite */
+        cipher_suites_length = 2;
+    } else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+        cipher_suites_length = *input << 8 | *(input + 1);
         input += 2;
     } else {
-        uint16_t cipher_suites_length = *input << 8 | *(input + 1);
-        input += 2;
+        return -1;
+    }
 
-        if (!(HAS_SPACE(cipher_suites_length)))
-            goto invalid_length;
+    if (!(HAS_SPACE(cipher_suites_length)))
+        goto invalid_length;
 
-        /* Cipher suites length should always be divisible by 2 */
-        if ((cipher_suites_length % 2) != 0) {
-            goto invalid_length;
-        }
+    /* Cipher suites length should always be divisible by 2 */
+    if ((cipher_suites_length % 2) != 0) {
+        goto invalid_length;
+    }
 
-        if (ssl_config.enable_ja3) {
-            int rc;
+    if (ssl_config.enable_ja3) {
+        int rc;
 
-            JA3Buffer *ja3_cipher_suites = Ja3BufferInit();
-            if (ja3_cipher_suites == NULL)
-                return -1;
+        JA3Buffer *ja3_cipher_suites = Ja3BufferInit();
+        if (ja3_cipher_suites == NULL)
+            return -1;
 
-            uint16_t processed_len = 0;
-            /* coverity[tainted_data] */
-            while (processed_len < cipher_suites_length)
-            {
-                if (!(HAS_SPACE(2))) {
-                    Ja3BufferFree(&ja3_cipher_suites);
-                    goto invalid_length;
-                }
+        uint16_t processed_len = 0;
+        /* coverity[tainted_data] */
+        while (processed_len < cipher_suites_length)
+        {
+            if (!(HAS_SPACE(2))) {
+                Ja3BufferFree(&ja3_cipher_suites);
+                goto invalid_length;
+            }
 
-                uint16_t cipher_suite = *input << 8 | *(input + 1);
-                input += 2;
+            uint16_t cipher_suite = *input << 8 | *(input + 1);
+            input += 2;
 
-                if (TLSDecodeValueIsGREASE(cipher_suite) != 1) {
-                    rc = Ja3BufferAddValue(&ja3_cipher_suites, cipher_suite);
-                    if (rc != 0) {
-                        return -1;
-                    }
+            if (TLSDecodeValueIsGREASE(cipher_suite) != 1) {
+                rc = Ja3BufferAddValue(&ja3_cipher_suites, cipher_suite);
+                if (rc != 0) {
+                    return -1;
                 }
-
-                processed_len += 2;
             }
 
-            rc = Ja3BufferAppendBuffer(&ssl_state->ja3_str, &ja3_cipher_suites);
-            if (rc == -1) {
-                return -1;
-            }
+            processed_len += 2;
+        }
 
-        } else {
-            /* Skip cipher suites */
-            input += cipher_suites_length;
+        rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+                                   &ja3_cipher_suites);
+        if (rc == -1) {
+            return -1;
         }
+
+    } else {
+        /* Skip cipher suites */
+        input += cipher_suites_length;
     }
 
     return (input - initial_input);
@@ -1113,14 +1115,20 @@ static inline int TLSDecodeHSHelloExtensions(SSLState *ssl_state,
     JA3Buffer *ja3_elliptic_curves = NULL;
     JA3Buffer *ja3_elliptic_curves_pf = NULL;
 
-    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
-            ssl_config.enable_ja3) {
+    if (ssl_config.enable_ja3) {
         ja3_extensions = Ja3BufferInit();
-        ja3_elliptic_curves = Ja3BufferInit();
-        ja3_elliptic_curves_pf = Ja3BufferInit();
-        if (ja3_extensions == NULL || ja3_elliptic_curves == NULL ||
-                ja3_elliptic_curves_pf == NULL)
+        if (ja3_extensions == NULL)
             goto error;
+
+        if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+            ja3_elliptic_curves = Ja3BufferInit();
+            if (ja3_elliptic_curves == NULL)
+                goto error;
+
+            ja3_elliptic_curves_pf = Ja3BufferInit();
+            if (ja3_elliptic_curves_pf == NULL)
+                goto error;
+        }
     }
 
     /* Extensions are optional (RFC5246 section 7.4.1.2) */
@@ -1239,8 +1247,7 @@ static inline int TLSDecodeHSHelloExtensions(SSLState *ssl_state,
             }
         }
 
-        if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
-                ssl_config.enable_ja3) {
+        if (ssl_config.enable_ja3) {
             if (TLSDecodeValueIsGREASE(ext_type) != 1) {
                 rc = Ja3BufferAddValue(&ja3_extensions, ext_type);
                 if (rc != 0)
@@ -1252,20 +1259,23 @@ static inline int TLSDecodeHSHelloExtensions(SSLState *ssl_state,
     }
 
 end:
-    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
-            ssl_config.enable_ja3) {
-        rc = Ja3BufferAppendBuffer(&ssl_state->ja3_str, &ja3_extensions);
+    if (ssl_config.enable_ja3) {
+        rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+                                   &ja3_extensions);
         if (rc == -1)
             goto error;
 
-        rc = Ja3BufferAppendBuffer(&ssl_state->ja3_str, &ja3_elliptic_curves);
-        if (rc == -1)
-            goto error;
+        if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+            rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+                                       &ja3_elliptic_curves);
+            if (rc == -1)
+                goto error;
 
-        rc = Ja3BufferAppendBuffer(&ssl_state->ja3_str,
-                                   &ja3_elliptic_curves_pf);
-        if (rc == -1)
-            goto error;
+            rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+                                       &ja3_elliptic_curves_pf);
+            if (rc == -1)
+                goto error;
+        }
     }
 
     return (input - initial_input);
@@ -1343,9 +1353,8 @@ static int TLSDecodeHandshakeHello(SSLState *ssl_state,
     if (ret < 0)
         goto end;
 
-    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
-            ssl_config.enable_ja3 && ssl_state->ja3_hash == NULL) {
-        ssl_state->ja3_hash = Ja3GenerateHash(ssl_state->ja3_str);
+    if (ssl_config.enable_ja3 && ssl_state->curr_connp->ja3_hash == NULL) {
+        ssl_state->curr_connp->ja3_hash = Ja3GenerateHash(ssl_state->curr_connp->ja3_str);
     }
 
 end:
@@ -2596,10 +2605,14 @@ static void SSLStateFree(void *p)
     if (ssl_state->server_connp.session_id)
         SCFree(ssl_state->server_connp.session_id);
 
-    if (ssl_state->ja3_str)
-        Ja3BufferFree(&ssl_state->ja3_str);
-    if (ssl_state->ja3_hash)
-        SCFree(ssl_state->ja3_hash);
+    if (ssl_state->client_connp.ja3_str)
+        Ja3BufferFree(&ssl_state->client_connp.ja3_str);
+    if (ssl_state->client_connp.ja3_hash)
+        SCFree(ssl_state->client_connp.ja3_hash);
+    if (ssl_state->server_connp.ja3_str)
+        Ja3BufferFree(&ssl_state->server_connp.ja3_str);
+    if (ssl_state->server_connp.ja3_hash)
+        SCFree(ssl_state->server_connp.ja3_hash);
 
     AppLayerDecoderEventsFreeEvents(&ssl_state->decoder_events);
 
index d58bcef0e01214d1f5b3d1b329dd448b9495a308..0c3133a6ae85a39373e75f2b63fac59df568fc41 100644 (file)
@@ -208,6 +208,9 @@ typedef struct SSLStateConnp_ {
 
     uint32_t cert_log_flag;
 
+    JA3Buffer *ja3_str;
+    char *ja3_hash;
+
     /* buffer for the tls record.
      * We use a malloced buffer, if the record is fragmented */
     uint8_t *trec;
@@ -240,9 +243,6 @@ typedef struct SSLState_ {
 
     uint32_t current_flags;
 
-    JA3Buffer *ja3_str;
-    char *ja3_hash;
-
     SSLStateConnp *curr_connp;
 
     SSLStateConnp client_connp;
index 41b580b5162382b64736c2a1d5a81c0192d3476f..daefa443b15c3452efef7f0dabf8c62a6514b9f3 100644 (file)
@@ -138,12 +138,12 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
     if (buffer->inspect == NULL) {
         const SSLState *ssl_state = (SSLState *)f->alstate;
 
-        if (ssl_state->ja3_hash == NULL) {
+        if (ssl_state->client_connp.ja3_hash == NULL) {
             return NULL;
         }
 
-        const uint32_t data_len = strlen(ssl_state->ja3_hash);
-        const uint8_t *data = (uint8_t *)ssl_state->ja3_hash;
+        const uint32_t data_len = strlen(ssl_state->client_connp.ja3_hash);
+        const uint8_t *data = (uint8_t *)ssl_state->client_connp.ja3_hash;
 
         InspectionBufferSetup(buffer, data, data_len);
         InspectionBufferApplyTransforms(buffer, transforms);
index cd92645cb7054a9df81a55fb6681a70ad1e82a5b..d253fc289ec89b7b20c6c643978bbfaa7b13c06b 100644 (file)
@@ -128,13 +128,13 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
     if (buffer->inspect == NULL) {
         const SSLState *ssl_state = (SSLState *)f->alstate;
 
-        if (ssl_state->ja3_str == NULL ||
-                ssl_state->ja3_str->data == NULL) {
+        if (ssl_state->client_connp.ja3_str == NULL ||
+                ssl_state->client_connp.ja3_str->data == NULL) {
             return NULL;
         }
 
-        const uint32_t data_len = strlen(ssl_state->ja3_str->data);
-        const uint8_t *data = (uint8_t *)ssl_state->ja3_str->data;
+        const uint32_t data_len = strlen(ssl_state->client_connp.ja3_str->data);
+        const uint8_t *data = (uint8_t *)ssl_state->client_connp.ja3_str->data;
 
         InspectionBufferSetup(buffer, data, data_len);
         InspectionBufferApplyTransforms(buffer, transforms);
index 9284f79f142e372cd7994fe5b9821621f102a1f4..9f92d24b227194b609ec80ea2c42e2b251d65ef1 100644 (file)
@@ -199,17 +199,18 @@ static void JsonTlsLogNotAfter(json_t *js, SSLState *ssl_state)
 
 static void JsonTlsLogJa3Hash(json_t *js, SSLState *ssl_state)
 {
-    if (ssl_state->ja3_hash != NULL) {
-        json_object_set_new(js, "hash", json_string(ssl_state->ja3_hash));
+    if (ssl_state->client_connp.ja3_hash != NULL) {
+        json_object_set_new(js, "hash",
+                            json_string(ssl_state->client_connp.ja3_hash));
     }
 }
 
 static void JsonTlsLogJa3String(json_t *js, SSLState *ssl_state)
 {
-    if ((ssl_state->ja3_str != NULL) &&
-            ssl_state->ja3_str->data != NULL) {
+    if ((ssl_state->client_connp.ja3_str != NULL) &&
+            ssl_state->client_connp.ja3_str->data != NULL) {
         json_object_set_new(js, "string",
-                            json_string(ssl_state->ja3_str->data));
+                            json_string(ssl_state->client_connp.ja3_str->data));
     }
 }
 
index 70702b112e6e52e9639de08341d07593edbb44c4..3e666a9aa3d80d2fd3d4e112bda4dc892cf0aff9 100644 (file)
@@ -106,7 +106,7 @@ static int DetectTlsJa3HashTest01(void)
     ssl_state = f.alstate;
     FAIL_IF_NULL(ssl_state);
 
-    FAIL_IF_NULL(ssl_state->ja3_hash);
+    FAIL_IF_NULL(ssl_state->client_connp.ja3_hash);
 
     SigMatchSignatures(&tv, de_ctx, det_ctx, p);
 
@@ -202,7 +202,7 @@ static int DetectTlsJa3HashTest02(void)
     ssl_state = f.alstate;
     FAIL_IF_NULL(ssl_state);
 
-    FAIL_IF_NULL(ssl_state->ja3_hash);
+    FAIL_IF_NULL(ssl_state->client_connp.ja3_hash);
 
     SigMatchSignatures(&tv, de_ctx, det_ctx, p);
 
index 913e0c864e9da8e7a5935733247ad074e408c55c..2363c8d8ad3bfbb12166e59beb4513b94cf2d97a 100644 (file)
@@ -105,8 +105,8 @@ static int DetectTlsJa3StringTest01(void)
     ssl_state = f.alstate;
     FAIL_IF_NULL(ssl_state);
 
-    FAIL_IF_NULL(ssl_state->ja3_str);
-    FAIL_IF_NULL(ssl_state->ja3_str->data);
+    FAIL_IF_NULL(ssl_state->client_connp.ja3_str);
+    FAIL_IF_NULL(ssl_state->client_connp.ja3_str->data);
 
     SigMatchSignatures(&tv, de_ctx, det_ctx, p);
 
index 28c90a2f48aac6051163771e527a1a424cf20062..7029e2f2c1717a5230aaf09619f6c7cf945a8abf 100644 (file)
@@ -73,11 +73,12 @@ static int Ja3GetHash(lua_State *luastate)
 
     SSLState *ssl_state = (SSLState *)state;
 
-    if (ssl_state->ja3_hash == NULL)
+    if (ssl_state->client_connp.ja3_hash == NULL)
         return LuaCallbackError(luastate, "error: no JA3 hash");
 
-    return LuaPushStringBuffer(luastate, (uint8_t *)ssl_state->ja3_hash,
-                               strlen(ssl_state->ja3_hash));
+    return LuaPushStringBuffer(luastate,
+                               (uint8_t *)ssl_state->client_connp.ja3_hash,
+                               strlen(ssl_state->client_connp.ja3_hash));
 }
 
 static int Ja3GetString(lua_State *luastate)
@@ -95,11 +96,13 @@ static int Ja3GetString(lua_State *luastate)
 
     SSLState *ssl_state = (SSLState *)state;
 
-    if (ssl_state->ja3_str == NULL || ssl_state->ja3_str->data == NULL)
+    if (ssl_state->client_connp.ja3_str == NULL ||
+            ssl_state->client_connp.ja3_str->data == NULL)
         return LuaCallbackError(luastate, "error: no JA3 str");
 
-    return LuaPushStringBuffer(luastate, (uint8_t *)ssl_state->ja3_str->data,
-                               ssl_state->ja3_str->used);
+    return LuaPushStringBuffer(luastate,
+                               (uint8_t *)ssl_state->client_connp.ja3_str->data,
+                               ssl_state->client_connp.ja3_str->used);
 }
 
 /** *\brief Register JA3 Lua extensions */