From bf3a43782954697843937ec0d17c00bae5722183 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 18 Jun 2024 22:05:53 +0200 Subject: [PATCH 1/5] eve/schema: minor enip reformat --- etc/schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/schema.json b/etc/schema.json index d25ad6e58b1c..4a4bf37e9457 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -1506,7 +1506,7 @@ "class_name": { "type": "string" }, - "multiple" : { + "multiple": { "type": "array", "minItems": 1, "items": { @@ -1628,7 +1628,7 @@ "status_extended_meaning": { "type": "string" }, - "multiple" : { + "multiple": { "type": "array", "minItems": 1, "items": { From 985c4a5640eb250724ed8a710ee16075cc64bd5b Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 31 May 2024 14:18:15 +0200 Subject: [PATCH 2/5] tls: store all ALPN records in the state For later logging and detection. --- src/app-layer-ssl.c | 72 ++++++++++++++++++++++++++++++--------------- src/app-layer-ssl.h | 6 ++++ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 21797d678847..6802fb18d743 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -1258,6 +1258,18 @@ static inline int TLSDecodeHSHelloExtensionSigAlgorithms( return -1; } +static void StoreALPN(SSLStateConnp *connp, const uint8_t *alpn, const uint32_t size) +{ + if (size > 0) { + SSLAlpns *a = SCCalloc(1, sizeof(*a) + size); + if (a != NULL) { + memcpy(a->alpn, alpn, size); + a->size = size; + TAILQ_INSERT_TAIL(&connp->alpns, a, next); + } + } +} + static inline int TLSDecodeHSHelloExtensionALPN( SSLState *ssl_state, const uint8_t *const initial_input, const uint32_t input_len) { @@ -1276,37 +1288,35 @@ static inline int TLSDecodeHSHelloExtensionALPN( if (!(HAS_SPACE(alpn_len))) goto invalid_length; - if (ssl_state->curr_connp->ja4 != NULL && - ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) { - /* We use 32 bits here to avoid potentially overflowing a value that - needs to be compared to an unsigned 16-bit value. */ - uint32_t alpn_processed_len = 0; - while (alpn_processed_len < alpn_len) { - uint8_t protolen = *input; - input += 1; - alpn_processed_len += 1; + /* We use 32 bits here to avoid potentially overflowing a value that + needs to be compared to an unsigned 16-bit value. */ + uint32_t alpn_processed_len = 0; + while (alpn_processed_len < alpn_len) { + uint8_t protolen = *input; + input += 1; + alpn_processed_len += 1; - if (!(HAS_SPACE(protolen))) - goto invalid_length; + if (!(HAS_SPACE(protolen))) + goto invalid_length; - /* Check if reading another protolen bytes would exceed the - overall ALPN length; if so, skip and continue */ - if (alpn_processed_len + protolen > ((uint32_t)alpn_len)) { - input += alpn_len - alpn_processed_len; - break; - } + /* Check if reading another protolen bytes would exceed the + overall ALPN length; if so, skip and continue */ + if (alpn_processed_len + protolen > ((uint32_t)alpn_len)) { + input += alpn_len - alpn_processed_len; + break; + } - /* Only record the first value for JA4 */ + /* Only record the first value for JA4 */ + if (ssl_state->curr_connp->ja4 != NULL && + ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) { if (alpn_processed_len == 1) { SCJA4SetALPN(ssl_state->curr_connp->ja4, (const char *)input, protolen); } - - alpn_processed_len += protolen; - input += protolen; } - } else { - /* Skip ALPN protocols */ - input += alpn_len; + StoreALPN(ssl_state->curr_connp, input, protolen); + + alpn_processed_len += protolen; + input += protolen; } return (input - initial_input); @@ -2839,7 +2849,9 @@ static void *SSLStateAlloc(void *orig_state, AppProto proto_orig) memset(ssl_state->client_connp.random, 0, TLS_RANDOM_LEN); memset(ssl_state->server_connp.random, 0, TLS_RANDOM_LEN); TAILQ_INIT(&ssl_state->server_connp.certs); + TAILQ_INIT(&ssl_state->server_connp.alpns); TAILQ_INIT(&ssl_state->client_connp.certs); + TAILQ_INIT(&ssl_state->client_connp.alpns); return (void *)ssl_state; } @@ -2930,6 +2942,18 @@ static void SSLStateFree(void *p) } TAILQ_INIT(&ssl_state->client_connp.certs); + SSLAlpns *a; + while ((a = TAILQ_FIRST(&ssl_state->server_connp.alpns))) { + TAILQ_REMOVE(&ssl_state->server_connp.alpns, a, next); + SCFree(a); + } + TAILQ_INIT(&ssl_state->server_connp.alpns); + while ((a = TAILQ_FIRST(&ssl_state->client_connp.alpns))) { + TAILQ_REMOVE(&ssl_state->client_connp.alpns, a, next); + SCFree(a); + } + TAILQ_INIT(&ssl_state->client_connp.alpns); + SCFree(ssl_state); } diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 90217b037c33..479ccf51921d 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -226,6 +226,11 @@ typedef struct SSLCertsChain_ { TAILQ_ENTRY(SSLCertsChain_) next; } SSLCertsChain; +typedef struct SSLAlpns_ { + TAILQ_ENTRY(SSLAlpns_) next; + uint32_t size; + uint8_t alpn[]; +} SSLAlpns; typedef struct SSLStateConnp_ { /* record length */ @@ -262,6 +267,7 @@ typedef struct SSLStateConnp_ { char *session_id; TAILQ_HEAD(, SSLCertsChain_) certs; + TAILQ_HEAD(, SSLAlpns_) alpns; uint8_t *certs_buffer; uint32_t certs_buffer_size; From d5aaca91d8e32dd42e9e3d46ad8db2a3267ab346 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 31 May 2024 14:33:31 +0200 Subject: [PATCH 3/5] eve/tls: log ALPN for client and server Part of the extended logging. Logs `client_alpns` and `server_alpns` arrays in the tls object. Ticket: #7055. --- doc/userguide/output/eve/eve-json-format.rst | 2 ++ src/output-json-tls.c | 21 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/doc/userguide/output/eve/eve-json-format.rst b/doc/userguide/output/eve/eve-json-format.rst index 9da9e82d3e2c..2ea003fb9614 100644 --- a/doc/userguide/output/eve/eve-json-format.rst +++ b/doc/userguide/output/eve/eve-json-format.rst @@ -1046,6 +1046,8 @@ If extended logging is enabled the following fields are also included: * "ja3": The JA3 fingerprint consisting of both a JA3 hash and a JA3 string * "ja3s": The JA3S fingerprint consisting of both a JA3 hash and a JA3 string * "ja4": The JA4 client fingerprint for TLS +* "client_alpns": array of strings with ALPN values +* "server_alpns": array of strings with ALPN values JA3 and JA4 must be enabled in the Suricata config file (set 'app-layer.protocols.tls.ja3-fingerprints'/'app-layer.protocols.tls.ja4-fingerprints' to 'yes'). diff --git a/src/output-json-tls.c b/src/output-json-tls.c index 821e76553936..406f6aca01c8 100644 --- a/src/output-json-tls.c +++ b/src/output-json-tls.c @@ -263,6 +263,24 @@ static void JsonTlsLogJa3S(JsonBuilder *js, SSLState *ssl_state) } } +static void JsonTlsLogAlpns(JsonBuilder *js, SSLStateConnp *connp, const char *object) +{ + if (TAILQ_EMPTY(&connp->alpns)) { + return; + } + + SSLAlpns *a = TAILQ_FIRST(&connp->alpns); + if (a == NULL) { + return; + } + + jb_open_array(js, object); + TAILQ_FOREACH (a, &connp->alpns, next) { + jb_append_string_from_bytes(js, a->alpn, a->size); + } + jb_close(js); +} + static void JsonTlsLogCertificate(JsonBuilder *js, SSLStateConnp *connp) { if (TAILQ_EMPTY(&connp->certs)) { @@ -457,6 +475,9 @@ static bool JsonTlsLogJSONExtendedAux(void *vtx, JsonBuilder *tjs) /* tls ja4 */ JsonTlsLogSCJA4(tjs, state); + JsonTlsLogAlpns(tjs, &state->client_connp, "client_alpns"); + JsonTlsLogAlpns(tjs, &state->server_connp, "server_alpns"); + if (HasClientCert(&state->client_connp)) { jb_open_object(tjs, "client"); JsonTlsLogClientCert(tjs, &state->client_connp, false, false); From 481d59c24e47f4bb08287fa8477d146381910e63 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 31 May 2024 15:05:16 +0200 Subject: [PATCH 4/5] eve/schema: update for alpn --- etc/schema.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/etc/schema.json b/etc/schema.json index 4a4bf37e9457..3f3e44562a6b 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -6139,6 +6139,20 @@ }, "additionalProperties": false }, + "client_alpns": { + "description": "TLS client ALPN field(s)", + "type": "array", + "items": { + "type": "string" + } + }, + "server_alpns": { + "description": "TLS server ALPN field(s)", + "type": "array", + "items": { + "type": "string" + } + }, "fingerprint": { "type": "string" }, From fde64ce7ea24066ab7c0a257377827300e57916a Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Sat, 1 Jun 2024 09:12:29 +0200 Subject: [PATCH 5/5] detect: add tls.alpn keyword Ticket: #7108. --- doc/userguide/rules/tls-keywords.rst | 13 +++ src/Makefile.am | 2 + src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-tls-alpn.c | 148 +++++++++++++++++++++++++++ src/detect-tls-alpn.h | 29 ++++++ 6 files changed, 195 insertions(+) create mode 100644 src/detect-tls-alpn.c create mode 100644 src/detect-tls-alpn.h diff --git a/doc/userguide/rules/tls-keywords.rst b/doc/userguide/rules/tls-keywords.rst index dbca6a3d5eab..0db3e1c049b6 100644 --- a/doc/userguide/rules/tls-keywords.rst +++ b/doc/userguide/rules/tls-keywords.rst @@ -319,3 +319,16 @@ Example:: alert tls any any -> any any (msg:"cert chain not value"; \ tls.cert_chain_len:!2; classtype:misc-activity; sid:4; rev:1;) + +tls.alpn +-------- + +Matches on the ALPN buffers. + +Example:: + + alert tls any any -> any any (msg:"TLS ALPN test"; \ + tls.alpn; content:"http/1.1"; sid:1;) + +``tls.alpn`` is a sticky buffer. + diff --git a/src/Makefile.am b/src/Makefile.am index 5b83d60cfd42..eb4e4e72053a 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -314,6 +314,7 @@ noinst_HEADERS = \ detect-template.h \ detect-template-rust-buffer.h \ detect-threshold.h \ + detect-tls-alpn.h \ detect-tls-cert-fingerprint.h \ detect-tls-cert-issuer.h \ detect-tls-cert-serial.h \ @@ -914,6 +915,7 @@ libsuricata_c_a_SOURCES = \ detect-template-rust-buffer.c \ detect-threshold.c \ detect-tls.c \ + detect-tls-alpn.c \ detect-tls-cert-fingerprint.c \ detect-tls-cert-issuer.c \ detect-tls-certs.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 5d4438485811..ada4278a5b0f 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -59,6 +59,7 @@ #include "detect-tls-cert-issuer.h" #include "detect-tls-cert-subject.h" #include "detect-tls-cert-serial.h" +#include "detect-tls-alpn.h" #include "detect-tls-subjectaltname.h" #include "detect-tls-random.h" #include "detect-tls-ja3-hash.h" @@ -562,6 +563,7 @@ void SigTableSetup(void) DetectTlsCertsRegister(); DetectTlsCertChainLenRegister(); DetectTlsSubjectAltNameRegister(); + DetectTlsAlpnRegister(); DetectTlsRandomRegister(); DetectTlsJa3HashRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 71af4c3a1659..f174fea0b6cd 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -128,6 +128,7 @@ enum DetectKeywordId { DETECT_AL_TLS_FINGERPRINT, DETECT_AL_TLS_STORE, DETECT_AL_TLS_CHAIN_LEN, + DETECT_TLS_ALPN, DETECT_AL_HTTP_COOKIE, DETECT_HTTP_COOKIE, diff --git a/src/detect-tls-alpn.c b/src/detect-tls-alpn.c new file mode 100644 index 000000000000..b4aa82f9c52a --- /dev/null +++ b/src/detect-tls-alpn.c @@ -0,0 +1,148 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Victor Julien + * + * Implements support for tls.alpn keyword. + */ + +#include "suricata-common.h" +#include "threads.h" +#include "decode.h" +#include "detect.h" + +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-content-inspection.h" +#include "detect-content.h" +#include "detect-tls-alpn.h" +#include "detect-engine-uint.h" + +#include "flow.h" +#include "flow-util.h" +#include "flow-var.h" + +#include "util-debug.h" +#include "util-spm.h" +#include "util-print.h" + +#include "stream-tcp.h" + +#include "app-layer.h" +#include "app-layer-ssl.h" +#include "util-profiling.h" + +static int DetectTlsAlpnSetup(DetectEngineCtx *, Signature *, const char *); +static InspectionBuffer *TlsAlpnGetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t index); + +static int g_tls_alpn_buffer_id = 0; + +/** + * \brief Registration function for keyword: tls.alpn + */ +void DetectTlsAlpnRegister(void) +{ + sigmatch_table[DETECT_TLS_ALPN].name = "tls.alpn"; + sigmatch_table[DETECT_TLS_ALPN].desc = "sticky buffer to match the TLS ALPN buffer"; + sigmatch_table[DETECT_TLS_ALPN].url = "/rules/tls-keywords.html#tls-alpn"; + sigmatch_table[DETECT_TLS_ALPN].Setup = DetectTlsAlpnSetup; + sigmatch_table[DETECT_TLS_ALPN].flags |= SIGMATCH_NOOPT; + sigmatch_table[DETECT_TLS_ALPN].flags |= SIGMATCH_INFO_STICKY_BUFFER; + + DetectAppLayerMultiRegister("tls.alpn", ALPROTO_TLS, SIG_FLAG_TOSERVER, 0, TlsAlpnGetData, 2, + TLS_STATE_IN_PROGRESS); + DetectAppLayerMultiRegister( + "tls.alpn", ALPROTO_TLS, SIG_FLAG_TOCLIENT, 0, TlsAlpnGetData, 2, TLS_STATE_CERT_READY); + + DetectBufferTypeSetDescriptionByName("tls.alpn", "TLS APLN"); + + DetectBufferTypeSupportsMultiInstance("tls.alpn"); + + g_tls_alpn_buffer_id = DetectBufferTypeGetByName("tls.alpn"); +} + +/** + * \brief This function setup the tls.alpn sticky buffer keyword + * + * \param de_ctx Pointer to the Detect Engine Context + * \param s Pointer to the Signature to which the keyword belongs + * \param str Should hold an empty string always + * + * \retval 0 On success + * \retval -1 On failure + */ +static int DetectTlsAlpnSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + if (DetectBufferSetActiveList(de_ctx, s, g_tls_alpn_buffer_id) < 0) + return -1; + + if (DetectSignatureSetAppProto(s, ALPROTO_TLS) < 0) + return -1; + + return 0; +} + +static InspectionBuffer *TlsAlpnGetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t idx) +{ + SCEnter(); + InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx); + if (buffer == NULL || buffer->initialized) + return buffer; + + const SSLState *ssl_state = (SSLState *)f->alstate; + const SSLStateConnp *connp; + + if (flags & STREAM_TOSERVER) { + connp = &ssl_state->client_connp; + } else { + connp = &ssl_state->server_connp; + } + + if (TAILQ_EMPTY(&connp->alpns)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + + SSLAlpns *a; + if (idx == 0) { + a = TAILQ_FIRST(&connp->alpns); + } else { + // TODO optimize ? + a = TAILQ_FIRST(&connp->alpns); + for (uint32_t i = 0; i < idx; i++) { + a = TAILQ_NEXT(a, next); + } + } + if (a == NULL) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + + InspectionBufferSetupMulti(buffer, transforms, a->alpn, a->size); + buffer->flags = DETECT_CI_FLAGS_SINGLE; + + SCReturnPtr(buffer, "InspectionBuffer"); +} diff --git a/src/detect-tls-alpn.h b/src/detect-tls-alpn.h new file mode 100644 index 000000000000..817b59f38f30 --- /dev/null +++ b/src/detect-tls-alpn.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Victor Julien + */ + +#ifndef SURICATA_DETECT_TLS_ALPN_H +#define SURICATA_DETECT_TLS_ALPN_H + +void DetectTlsAlpnRegister(void); + +#endif /* SURICATA_DETECT_TLS_ALPN_H */