mirror of
https://github.com/OISF/suricata.git
synced 2026-10-12 02:07:10 +00:00
alert/reference: Optionally add reference(s)
Issue: 4974 Optionally include rule references with the alert. Since there can be multiple reference keywords, they are collected into an array.
This commit is contained in:
committed by
Victor Julien
parent
d3eb6569d2
commit
9bc468e63a
@@ -288,6 +288,13 @@
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"references": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"source": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
+54
-26
@@ -43,7 +43,10 @@
|
||||
#include "util-byte.h"
|
||||
#include "util-debug.h"
|
||||
|
||||
#define PARSE_REGEX "^\\s*([A-Za-z0-9]+)\\s*,\"?\\s*\"?\\s*([a-zA-Z0-9\\-_\\.\\/\\?\\=]+)\"?\\s*\"?"
|
||||
/* Breakout key and scheme (optional) and domain/path (mandatory) */
|
||||
#define PARSE_REGEX \
|
||||
"^\\s*([A-Za-z0-9]+)\\s*,\"?\\s*\"?\\s*([a-zA-Z]+:\\/\\/)?([a-zA-Z0-9\\-_\\.\\/" \
|
||||
"\\?\\=]+)\"?\\s*\"?"
|
||||
|
||||
static DetectParseRegex parse_regex;
|
||||
|
||||
@@ -74,6 +77,9 @@ void DetectReferenceFree(DetectReference *ref)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
if (ref->key)
|
||||
SCFree(ref->key);
|
||||
|
||||
if (ref->reference != NULL) {
|
||||
SCFree(ref->reference);
|
||||
}
|
||||
@@ -98,11 +104,12 @@ static DetectReference *DetectReferenceParse(const char *rawstr, DetectEngineCtx
|
||||
int res = 0;
|
||||
size_t pcre2len;
|
||||
char key[REFERENCE_SYSTEM_NAME_MAX] = "";
|
||||
char content[REFERENCE_CONTENT_NAME_MAX] = "";
|
||||
char scheme[REFERENCE_SYSTEM_NAME_MAX] = "";
|
||||
char uri[REFERENCE_CONTENT_NAME_MAX] = "";
|
||||
|
||||
pcre2_match_data *match = NULL;
|
||||
int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
|
||||
if (ret < 2) {
|
||||
if (ret != 4) {
|
||||
SCLogError("Unable to parse \"reference\" "
|
||||
"keyword argument - \"%s\". Invalid argument.",
|
||||
rawstr);
|
||||
@@ -118,51 +125,72 @@ static DetectReference *DetectReferenceParse(const char *rawstr, DetectEngineCtx
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Position 1 = key (mandatory) */
|
||||
pcre2len = sizeof(key);
|
||||
res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)key, &pcre2len);
|
||||
if (res < 0) {
|
||||
SCLogError("pcre2_substring_copy_bynumber failed");
|
||||
SCLogError("pcre2_substring_copy_bynumber key failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
pcre2len = sizeof(content);
|
||||
res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)content, &pcre2len);
|
||||
/* Position 2 = scheme (optional) */
|
||||
pcre2len = sizeof(scheme);
|
||||
(void)pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)scheme, &pcre2len);
|
||||
|
||||
/* Position 3 = domain-path (mandatory) */
|
||||
pcre2len = sizeof(uri);
|
||||
res = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)uri, &pcre2len);
|
||||
if (res < 0) {
|
||||
SCLogError("pcre2_substring_copy_bynumber failed");
|
||||
SCLogError("pcre2_substring_copy_bynumber domain-path failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strlen(key) == 0 || strlen(content) == 0)
|
||||
int ref_len = strlen(uri);
|
||||
/* no key, reference -- return an error */
|
||||
if (strlen(key) == 0 || ref_len == 0)
|
||||
goto error;
|
||||
|
||||
SCRConfReference *lookup_ref_conf = SCRConfGetReference(key, de_ctx);
|
||||
if (lookup_ref_conf != NULL) {
|
||||
ref->key = lookup_ref_conf->url;
|
||||
if (strlen(scheme)) {
|
||||
SCLogConfig("scheme value %s overrides key %s", scheme, key);
|
||||
ref->key = SCStrdup(scheme);
|
||||
/* already bound checked to be REFERENCE_SYSTEM_NAME_MAX or less */
|
||||
ref->key_len = (uint16_t)strlen(scheme);
|
||||
} else {
|
||||
if (SigMatchStrictEnabled(DETECT_REFERENCE)) {
|
||||
SCLogError("unknown reference key \"%s\"", key);
|
||||
goto error;
|
||||
|
||||
SCRConfReference *lookup_ref_conf = SCRConfGetReference(key, de_ctx);
|
||||
if (lookup_ref_conf != NULL) {
|
||||
ref->key = SCStrdup(lookup_ref_conf->url);
|
||||
/* already bound checked to be REFERENCE_SYSTEM_NAME_MAX or less */
|
||||
ref->key_len = (uint16_t)strlen(ref->key);
|
||||
} else {
|
||||
if (SigMatchStrictEnabled(DETECT_REFERENCE)) {
|
||||
SCLogError("unknown reference key \"%s\"", key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
SCLogWarning("unknown reference key \"%s\"", key);
|
||||
|
||||
char str[2048];
|
||||
snprintf(str, sizeof(str), "config reference: %s undefined\n", key);
|
||||
|
||||
if (SCRConfAddReference(de_ctx, str) < 0)
|
||||
goto error;
|
||||
lookup_ref_conf = SCRConfGetReference(key, de_ctx);
|
||||
if (lookup_ref_conf == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
SCLogWarning("unknown reference key \"%s\"", key);
|
||||
|
||||
char str[2048];
|
||||
snprintf(str, sizeof(str), "config reference: %s undefined\n", key);
|
||||
|
||||
if (SCRConfAddReference(de_ctx, str) < 0)
|
||||
goto error;
|
||||
lookup_ref_conf = SCRConfGetReference(key, de_ctx);
|
||||
if (lookup_ref_conf == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* make a copy so we can free pcre's substring */
|
||||
ref->reference = SCStrdup(content);
|
||||
ref->reference = SCStrdup(uri);
|
||||
if (ref->reference == NULL) {
|
||||
SCLogError("strdup failed: %s", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* already bound checked to be REFERENCE_CONTENT_NAME_MAX or less */
|
||||
ref->reference_len = (uint16_t)ref_len;
|
||||
|
||||
pcre2_match_data_free(match);
|
||||
/* free the substrings */
|
||||
SCReturnPtr(ref, "Reference");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2010 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
|
||||
@@ -32,6 +32,13 @@ typedef struct DetectReference_ {
|
||||
char *key;
|
||||
/* reference data */
|
||||
char *reference;
|
||||
|
||||
/*
|
||||
* These have been length checked against REFERENCE_SYSTEM_NAME_MAX,
|
||||
* and REFERENCE_CONTENT_NAME_MAX
|
||||
*/
|
||||
uint16_t key_len;
|
||||
uint16_t reference_len;
|
||||
/* next reference in the signature */
|
||||
struct DetectReference_ *next;
|
||||
} DetectReference;
|
||||
|
||||
+29
-1
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013-2023 Open Information Security Foundation
|
||||
/* Copyright (C) 2013-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
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "util-print.h"
|
||||
#include "util-optimize.h"
|
||||
#include "util-buffer.h"
|
||||
#include "util-reference-config.h"
|
||||
#include "util-validate.h"
|
||||
|
||||
#include "action-globals.h"
|
||||
@@ -83,6 +84,7 @@
|
||||
#define LOG_JSON_WEBSOCKET_PAYLOAD BIT_U16(11)
|
||||
#define LOG_JSON_WEBSOCKET_PAYLOAD_BASE64 BIT_U16(12)
|
||||
#define LOG_JSON_PAYLOAD_LENGTH BIT_U16(13)
|
||||
#define LOG_JSON_REFERENCE BIT_U16(14)
|
||||
|
||||
#define METADATA_DEFAULTS ( LOG_JSON_FLOW | \
|
||||
LOG_JSON_APP_LAYER | \
|
||||
@@ -169,6 +171,27 @@ static void AlertJsonSourceTarget(const Packet *p, const PacketAlert *pa,
|
||||
jb_close(js);
|
||||
}
|
||||
|
||||
static void AlertJsonReference(const PacketAlert *pa, JsonBuilder *jb)
|
||||
{
|
||||
if (!pa->s->references) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DetectReference *kv = pa->s->references;
|
||||
jb_open_array(jb, "references");
|
||||
while (kv) {
|
||||
/* Note that the key and reference sizes have been bound
|
||||
* checked during parsing
|
||||
*/
|
||||
const size_t size_needed = kv->key_len + kv->reference_len + 1;
|
||||
char kv_store[size_needed];
|
||||
snprintf(kv_store, size_needed, "%s%s", kv->key, kv->reference);
|
||||
jb_append_string(jb, kv_store);
|
||||
kv = kv->next;
|
||||
}
|
||||
jb_close(jb);
|
||||
}
|
||||
|
||||
static void AlertJsonMetadata(AlertJsonOutputCtx *json_output_ctx,
|
||||
const PacketAlert *pa, JsonBuilder *js)
|
||||
{
|
||||
@@ -221,6 +244,10 @@ void AlertJsonHeader(void *ctx, const Packet *p, const PacketAlert *pa, JsonBuil
|
||||
AlertJsonSourceTarget(p, pa, js, addr);
|
||||
}
|
||||
|
||||
if ((json_output_ctx != NULL) && (flags & LOG_JSON_REFERENCE)) {
|
||||
AlertJsonReference(pa, js);
|
||||
}
|
||||
|
||||
if ((json_output_ctx != NULL) && (flags & LOG_JSON_RULE_METADATA)) {
|
||||
AlertJsonMetadata(json_output_ctx, pa, js);
|
||||
}
|
||||
@@ -902,6 +929,7 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx,
|
||||
SetFlag(rule_metadata, "raw", LOG_JSON_RULE, &flags);
|
||||
SetFlag(rule_metadata, "metadata", LOG_JSON_RULE_METADATA,
|
||||
&flags);
|
||||
SetFlag(rule_metadata, "reference", LOG_JSON_REFERENCE, &flags);
|
||||
}
|
||||
SetFlag(metadata, "flow", LOG_JSON_FLOW, &flags);
|
||||
SetFlag(metadata, "app-layer", LOG_JSON_APP_LAYER, &flags);
|
||||
|
||||
@@ -168,6 +168,19 @@ outputs:
|
||||
# payload-length: yes # enable dumping payload length, including the gaps
|
||||
# packet: yes # enable dumping of packet (without stream segments)
|
||||
# metadata: no # enable inclusion of app layer metadata with alert. Default yes
|
||||
# If you want metadata, use:
|
||||
# metadata:
|
||||
# Include the decoded application layer (ie. http, dns)
|
||||
#app-layer: true
|
||||
# Log the current state of the flow record.
|
||||
#flow: true
|
||||
#rule:
|
||||
# Log the metadata field from the rule in a structured
|
||||
# format.
|
||||
#metadata: true
|
||||
# Log the raw rule text.
|
||||
#raw: false
|
||||
#reference: false # include reference information from the rule
|
||||
# http-body: yes # Requires metadata; enable dumping of HTTP body in Base64
|
||||
# http-body-printable: yes # Requires metadata; enable dumping of HTTP body in printable format
|
||||
# websocket-payload: yes # Requires metadata; enable dumping of WebSocket Payload in Base64
|
||||
|
||||
Reference in New Issue
Block a user