app-layer-ssl: generate JA3S fingerprints

Generate JA3S fingerprints based on fields in the ServerHello record.
pull/3874/head
Mats Klepsland 8 years ago
parent 3a16009966
commit a4471987ba

@ -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);

@ -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;

@ -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);

@ -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);

@ -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));
}
}

@ -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);

@ -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);

@ -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 */

Loading…
Cancel
Save