From: Eric Leblond Date: Sun, 2 Mar 2025 16:34:06 +0000 (+0100) Subject: detect/pcre: add extraction for alert X-Git-Tag: suricata-8.0.0-rc1~69 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3fbc7187289d4430c250925b7c397ec649897583;p=thirdparty%2Fsuricata.git detect/pcre: add extraction for alert With datajson infrastructure in place, it is now possible to add data in the extra information section. Following an idea by Jason Ish, this patch adds the feature for pcre extraction. A PCRE such as pcre:"/(?P[a-zA-Z]+)\//" will add the content of the captured group to alert.extra.ua. --- diff --git a/src/detect-pcre.c b/src/detect-pcre.c index ab6fe42c40..986fbfa501 100644 --- a/src/detect-pcre.c +++ b/src/detect-pcre.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2022 Open Information Security Foundation +/* Copyright (C) 2007-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 @@ -155,6 +155,46 @@ void DetectPcreRegister (void) #endif } +static void DetectAlertStoreMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, uint32_t idx, + uint8_t *str_ptr, uint16_t capture_len) +{ + /* We need the key */ + const char *json_key = VarNameStoreLookupById(idx, VAR_TYPE_ALERT_VAR); + + if (json_key == NULL) { + SCFree(str_ptr); + return; + } + + SCLogDebug("json key: %s", json_key); + /* Setup the data*/ + if ((det_ctx->json_content_len < SIG_JSON_CONTENT_ARRAY_LEN) && + (capture_len + strlen(json_key) + 5 < SIG_JSON_CONTENT_ITEM_LEN)) { + SCJsonBuilder *js = SCJbNewObject(); + if (unlikely(js == NULL)) { + SCFree(str_ptr); + return; + } + SCJbSetStringFromBytes(js, json_key, str_ptr, capture_len); + uint32_t js_len = SCJbLen(js); + if (js_len > SIG_JSON_CONTENT_ITEM_LEN) { + SCLogDebug("Captured length is too long for JSON."); + SCFree(str_ptr); + return; + } + /* Copy js but skip the starting curly bracket to just get the inner data */ + memcpy(det_ctx->json_content[det_ctx->json_content_len].json_content, SCJbPtr(js) + 1, + js_len - 1); + /* end the string as we have used memcpy */ + det_ctx->json_content[det_ctx->json_content_len].json_content[js_len - 1] = 0; + det_ctx->json_content[det_ctx->json_content_len].id = (void *)s; + det_ctx->json_content_len++; + SCJbFree(js); + } + + SCFree(str_ptr); +} + /** * \brief Match a regex on a single payload. * @@ -279,6 +319,11 @@ int DetectPcrePayloadMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, } else if (pe->captypes[x] == VAR_TYPE_FLOW_VAR && f != NULL) { (void)DetectVarStoreMatch(det_ctx, pe->capids[x], (uint8_t *)str_ptr, (uint16_t)capture_len, DETECT_VAR_TYPE_FLOW_POSTMATCH); + + } else if (pe->captypes[x] == VAR_TYPE_ALERT_VAR) { + (void)DetectAlertStoreMatch(det_ctx, s, pe->capids[x], (uint8_t *)str_ptr, + (uint16_t)capture_len); + } else { BUG_ON(1); // Impossible captype SCFree(str_ptr); @@ -362,20 +407,32 @@ static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, int cut_capture = 0; char *fcap = strstr(regexstr, "flow:"); char *pcap = strstr(regexstr, "pkt:"); + char *acap = strstr(regexstr, "alert:"); /* take the size of the whole input as buffer size for the regex we will * extract below. Add 1 to please Coverity's alloc_strlen test. */ size_t slen = strlen(regexstr) + 1; - if (fcap || pcap) { + if (fcap || pcap || acap) { SCLogDebug("regexstr %s", regexstr); - if (fcap && !pcap) + bool a_set = false; + cut_capture = 0; + if (fcap) { + a_set = true; cut_capture = (int)(fcap - regexstr); - else if (pcap && !fcap) - cut_capture = (int)(pcap - regexstr); - else { - BUG_ON(pcap == NULL); // added to assist cppcheck - BUG_ON(fcap == NULL); - cut_capture = (int)MIN((pcap - regexstr), (fcap - regexstr)); + } + if (pcap) { + if (a_set) + cut_capture = (int)MIN(cut_capture, (pcap - regexstr)); + else { + cut_capture = (int)(pcap - regexstr); + a_set = true; + } + } + if (acap) { + if (a_set) + cut_capture = MIN(cut_capture, (acap - regexstr)); + else + cut_capture = (int)(acap - regexstr); } SCLogDebug("cut_capture %d", cut_capture); @@ -760,6 +817,12 @@ static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx, SCLogDebug("id %u type %u", pd->capids[pd->idx], pd->captypes[pd->idx]); pd->idx++; + } else if (strncmp(name_array[name_idx], "alert:", 6) == 0) { + pd->capids[pd->idx] = + VarNameStoreRegister(name_array[name_idx] + 6, VAR_TYPE_ALERT_VAR); + pd->captypes[pd->idx] = VAR_TYPE_ALERT_VAR; + pd->idx++; + } else { SCLogError(" pkt/flow " "var capture names must start with 'pkt:' or 'flow:'"); @@ -825,6 +888,10 @@ static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx, pd->capids[pd->idx] = VarNameStoreRegister((char *)capture_str, VAR_TYPE_FLOW_VAR); pd->captypes[pd->idx] = VAR_TYPE_FLOW_VAR; pd->idx++; + } else if (strcmp(type_str, "alert") == 0) { + pd->capids[pd->idx] = VarNameStoreRegister((char *)capture_str, VAR_TYPE_ALERT_VAR); + pd->captypes[pd->idx] = VAR_TYPE_ALERT_VAR; + pd->idx++; } //SCLogNotice("pd->capname %s", pd->capname); diff --git a/src/util-var.h b/src/util-var.h index 9b40c86921..498a1f0cd0 100644 --- a/src/util-var.h +++ b/src/util-var.h @@ -47,6 +47,7 @@ enum VarTypes { VAR_TYPE_IPPAIR_VAR, VAR_TYPE_TX_BIT, + VAR_TYPE_ALERT_VAR, }; typedef struct GenericVar_ {