diff --git a/doc/userguide/output/eve/eve-json-format.rst b/doc/userguide/output/eve/eve-json-format.rst index 978d5cbfa24d..9d31a5a5d2ae 100644 --- a/doc/userguide/output/eve/eve-json-format.rst +++ b/doc/userguide/output/eve/eve-json-format.rst @@ -89,23 +89,17 @@ generated the event. Event type: Alert ----------------- -Field action -~~~~~~~~~~~~ - -Possible values: "allowed" and "blocked" - -Example: - -:: +This field contains data about a signature that matched, such as +``signature_id`` (``sid`` in the rule) and the ``signature`` (``msg`` in the +rule). - - "action":"allowed" - -Action is set to "allowed" unless a rule used the "drop" action and Suricata is in IPS mode, or when the rule used the "reject" action. - -It can also contain information about Source and Target of the attack in the alert.source and alert.target field if target keyword is used in +It can also contain information about Source and Target of the attack in the +``alert.source`` and ``alert.target`` field if target keyword is used in the signature. +This event will also have the ``pcap_cnt`` field, when running in pcap mode, to +indicate which packet triggered the signature. + :: "alert": { @@ -147,6 +141,37 @@ the signature. } }, +Action field +~~~~~~~~~~~~ + +Possible values: "allowed" and "blocked". + +Example: + +:: + + "action":"allowed" + +Action is set to "allowed" unless a rule used the "drop" action and Suricata is +in IPS mode, or when the rule used the "reject" action. It is important to note +that this does not necessarily indicate the final verdict for a given packet or +flow, since one packet may match on several rules. + +Verdict Field +~~~~~~~~~~~~~ + +Possible values are "accept", "drop" or "reject". + +Example: + +:: + + "verdict":"drop" + +Verdict is the final action that will be applied to a given packet, based on all +the signatures triggered by it. In IPS mode, all values are possible. In IDS +mode, verdict is only present if its value is "reject". + Pcap Field ~~~~~~~~~~ @@ -154,6 +179,38 @@ If pcap log capture is active in `multi` mode, a `capture_file` key will be adde with value being the full path of the pcap file where the corresponding packets have been extracted. +Eventy type: Verdict +-------------------- + +The "verdict" event indicates the final decision by the engine for a given +packet that triggered alerts. This is especially useful for scenarios in which +there are several alerts with different, conflicting actions, as it will state +what was the prevailing action and whether the packet was also dropped or any +other outcomes (as it happens with the ``reject`` action, for instance). + +Examples +~~~~~~~~ + +:: + + "verdict": { + "action": "alert" + "reject-target": "both" + "reject": [ "tcp-reset", "icmp-prohib", "user defined" ] + } + +Fields +~~~~~~ + +* "action": the action associated with the alert, and performed by the engine. + Possible values: ``alert``, ``pass``, ``drop``. +* "reject-target": (optional) dependent on Engine mode (IDS or IPS) and type of reject + (cf :ref:`actions`). Possible values: ``source``, ``destination``, ``both``. +* "reject": (optional) ``["tcp-reset", "icmp-prohib", "user defined"]`` depending on + flow protocol and user settings. + +.. note:: ``reject`` is only logged for ``reject`` rules. + Event type: Anomaly ------------------- @@ -2532,4 +2589,4 @@ Example of DHCP log entry (extended logging enabled): "rebinding_time":43200, "client_id":"54:ee:75:51:e0:66", "dns_servers":["192.168.1.50","192.168.1.49"] - } \ No newline at end of file + } diff --git a/doc/userguide/output/eve/eve-json-output.rst b/doc/userguide/output/eve/eve-json-output.rst index c6d7ec395166..90a1bb2b9552 100644 --- a/doc/userguide/output/eve/eve-json-output.rst +++ b/doc/userguide/output/eve/eve-json-output.rst @@ -264,6 +264,19 @@ enabled, then the log gets more verbose. By using ``custom`` it is possible to select which TLS fields to log. +Drops +~~~~~ + +Drops are event types logged when the engine drops a packet. + +Config:: + + - drop: + alerts: yes # log alerts that caused drops + flows: all # start or all: 'start' logs only a single drop + # per flow direction. All logs each dropped pkt. + + Date modifiers in filename ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/etc/schema.json b/etc/schema.json index 3fa763603ae3..0aad03c3dcb3 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -94,6 +94,11 @@ "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+[+\\-]\\d+$" }, + "verdict": { + "type": "object", + "additionalProperties": true, + "$ref": "#/$defs/verdict_type" + }, "direction": { "type": "string" }, @@ -262,6 +267,9 @@ "items": { "type": "string" } + }, + "verdict": { + "$ref": "#/$defs/verdict_type" } }, "additionalProperties": true @@ -1289,6 +1297,9 @@ }, "reason": { "type": "string" + }, + "verdict": { + "$ref": "#/$defs/verdict_type" } }, "additionalProperties": false @@ -5482,6 +5493,20 @@ "$comment": "Definition for TLS date formats", "type": "string", "pattern": "^[1-2]\\d{3}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$" + }, + "verdict_type": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "reject": { + "type": "string" + }, + "reject-target": { + "type": "string" + } + } } } } diff --git a/src/Makefile.am b/src/Makefile.am index 48a5ce850ce2..4c23717a89da 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -427,6 +427,7 @@ noinst_HEADERS = \ output-json-template.h \ output-json-tftp.h \ output-json-tls.h \ + output-json-verdict.h \ output-eve-syslog.h \ output-lua.h \ output-packet.h \ @@ -1036,6 +1037,7 @@ libsuricata_c_a_SOURCES = \ output-json-template.c \ output-json-tftp.c \ output-json-tls.c \ + output-json-verdict.c \ output-eve-syslog.c \ output-lua.c \ output-packet.c \ diff --git a/src/decode.c b/src/decode.c index 2e4c448eb7b7..a91ea2606694 100644 --- a/src/decode.c +++ b/src/decode.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2022 Open Information Security Foundation +/* Copyright (C) 2007-2023 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 diff --git a/src/output-json-alert.c b/src/output-json-alert.c index e0ea7545ab97..d3e9db809049 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2013-2022 Open Information Security Foundation +/* Copyright (C) 2013-2023 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 @@ -78,6 +78,7 @@ #include "output-json-modbus.h" #include "output-json-frame.h" #include "output-json-quic.h" +#include "output-json-verdict.h" #include "util-byte.h" #include "util-privs.h" @@ -101,6 +102,7 @@ #define LOG_JSON_HTTP_BODY_BASE64 BIT_U16(7) #define LOG_JSON_RULE_METADATA BIT_U16(8) #define LOG_JSON_RULE BIT_U16(9) +#define LOG_JSON_VERDICT BIT_U16(10) #define METADATA_DEFAULTS ( LOG_JSON_FLOW | \ LOG_JSON_APP_LAYER | \ @@ -828,6 +830,10 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) jb_set_string(jb, "capture_file", pcap_filename); } + if (json_output_ctx->flags & LOG_JSON_VERDICT) { + GetVerdictJsonInfo(jb, p); + } + OutputJsonBuilderBuffer(jb, aft->ctx); jb_free(jb); } @@ -1016,6 +1022,7 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx, SetFlag(conf, "payload-printable", LOG_JSON_PAYLOAD, &flags); SetFlag(conf, "http-body-printable", LOG_JSON_HTTP_BODY, &flags); SetFlag(conf, "http-body", LOG_JSON_HTTP_BODY_BASE64, &flags); + SetFlag(conf, "verdict", LOG_JSON_VERDICT, &flags); /* Check for obsolete flags and warn that they have no effect. */ static const char *deprecated_flags[] = { "http", "tls", "ssh", "smtp", "dnp3", "app-layer", diff --git a/src/output-json-alert.h b/src/output-json-alert.h index 0a5af4268a09..5aaa034953f1 100644 --- a/src/output-json-alert.h +++ b/src/output-json-alert.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013-2014 Open Information Security Foundation +/* Copyright (C) 2013-2023 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 diff --git a/src/output-json-drop.c b/src/output-json-drop.c index 4e6048b53336..53ab0dfa35a9 100644 --- a/src/output-json-drop.c +++ b/src/output-json-drop.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2021 Open Information Security Foundation +/* Copyright (C) 2007-2023 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 @@ -45,6 +45,7 @@ #include "output-json.h" #include "output-json-alert.h" #include "output-json-drop.h" +#include "output-json-verdict.h" #include "util-unittest.h" #include "util-unittest-helper.h" @@ -60,7 +61,8 @@ #define MODULE_NAME "JsonDropLog" -#define LOG_DROP_ALERTS 1 +#define LOG_DROP_ALERTS BIT_U8(1) +#define LOG_DROP_VERDICT BIT_U8(2) typedef struct JsonDropOutputCtx_ { uint8_t flags; @@ -82,7 +84,7 @@ static int g_droplog_flows_start = 1; * \param tv Pointer the current thread variables * \param p Pointer the packet which is being logged * - * \return return TM_EODE_OK on success + * \return return TM_ECODE_OK on success */ static int DropLogJSON (JsonDropLogThread *aft, const Packet *p) { @@ -158,6 +160,10 @@ static int DropLogJSON (JsonDropLogThread *aft, const Packet *p) /* Close drop. */ jb_close(js); + if (aft->drop_ctx->flags & LOG_DROP_VERDICT) { + GetVerdictJsonInfo(js, p); + } + if (aft->drop_ctx->flags & LOG_DROP_ALERTS) { int logged = 0; int i; @@ -273,7 +279,7 @@ static OutputInitResult JsonDropLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ const char *extended = ConfNodeLookupChildValue(conf, "alerts"); if (extended != NULL) { if (ConfValIsTrue(extended)) { - drop_ctx->flags = LOG_DROP_ALERTS; + drop_ctx->flags |= LOG_DROP_ALERTS; } } extended = ConfNodeLookupChildValue(conf, "flows"); @@ -287,6 +293,12 @@ static OutputInitResult JsonDropLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ "'flow' are 'start' and 'all'"); } } + extended = ConfNodeLookupChildValue(conf, "verdict"); + if (extended != NULL) { + if (ConfValIsTrue(extended)) { + drop_ctx->flags |= LOG_DROP_VERDICT; + } + } } drop_ctx->eve_ctx = ajt; @@ -306,7 +318,7 @@ static OutputInitResult JsonDropLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ * \param data Pointer to the droplog struct * \param p Pointer the packet which is being logged * - * \retval 0 on succes + * \retval 0 on success */ static int JsonDropLogger(ThreadVars *tv, void *thread_data, const Packet *p) { diff --git a/src/output-json-verdict.c b/src/output-json-verdict.c new file mode 100644 index 000000000000..147caef8dc1f --- /dev/null +++ b/src/output-json-verdict.c @@ -0,0 +1,228 @@ +/* Copyright (C) 2023 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 Juliana Fajardini + * + * JSON Verdict log module to log the matched rules' final verdict on the packet + * information + * + */ + +#include "action-globals.h" + +#include "output.h" +#include "output-json.h" +#include "output-json-alert.h" +#include "output-json-verdict.h" + +#include "packet.h" + +#include "suricata.h" +#include "suricata-common.h" + +#define MODULE_NAME "JsonVerdictLog" + +typedef struct JsonVerdictOutputCtx_ { + uint8_t flags; + OutputJsonCtx *eve_ctx; +} JsonVerdictOutputCtx; + +typedef struct JsonVerdictLogThread_ { + JsonVerdictOutputCtx *verdict_ctx; + OutputJsonThreadCtx *ctx; +} JsonVerdictLogThread; + +void GetVerdictJsonInfo(JsonBuilder *jb, const Packet *p) +{ + jb_open_object(jb, "verdict"); + /* add verdict info */ + if (PacketCheckAction(p, ACTION_DROP) && EngineModeIsIPS()) { + JB_SET_STRING(jb, "action", "drop"); + } else if (PacketCheckAction(p, ACTION_REJECT_ANY)) { + // check rule to define type of reject packet sent + if (EngineModeIsIPS()) { + JB_SET_STRING(jb, "action", "drop"); + } else { + JB_SET_STRING(jb, "action", "alert"); + } + if (p->action & ACTION_REJECT || p->action) { + JB_SET_STRING(jb, "reject-target", "source"); + } else if (p->action & ACTION_REJECT_DST) { + JB_SET_STRING(jb, "reject-target", "destination"); + } else if (p->action & ACTION_REJECT_BOTH) { + JB_SET_STRING(jb, "reject-target", "both"); + } + JB_SET_STRING(jb, "reject", "[tcp-reset, icmp-prohib, user defined]"); + } else if (PacketCheckAction(p, ACTION_PASS)) { + JB_SET_STRING(jb, "action", "pass"); + } else { + // TODO make sure we don't have a situation where this wouldn't work + JB_SET_STRING(jb, "action", "alert"); + } + + /* Close verdict */ + jb_close(jb); + + return; +} + +static int VerdictJson(JsonVerdictLogThread *vlt, const Packet *p) +{ + JsonVerdictOutputCtx *verdict_ctx = vlt->verdict_ctx; + + JsonAddrInfo addr = json_addr_info_zero; + JsonAddrInfoInit(p, LOG_DIR_PACKET, &addr); + + JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_PACKET, "verdict", &addr, verdict_ctx->eve_ctx); + + if (unlikely(jb == NULL)) + return TM_ECODE_OK; + + GetVerdictJsonInfo(jb, p); + + OutputJsonBuilderBuffer(jb, vlt->ctx); + jb_free(jb); + + return TM_ECODE_OK; +} + +/** + * \brief Log the final verdict for a packet, based on matched rules + * + * \param tv Pointer to the current thread variables + * \param thread_data Pointer to the verdict log structure + * \param p Pointer to the packet that stores the info on verdict + * + * \return 0 on success + */ +static int JsonVerdictLogger(ThreadVars *tv, void *thread_data, const Packet *p) +{ + JsonVerdictLogThread *vlt = thread_data; + + int r = VerdictJson(vlt, p); + + if (r < 0) { + return -1; + } + + return 0; +} + +static int JsonVerdictLogCondition(ThreadVars *tv, void *thread_data, const Packet *p) +{ + if (p->alerts.cnt > 0) { + return TRUE; + } + + return FALSE; +} + +static TmEcode JsonVerdictLogThreadInit(ThreadVars *t, const void *initdata, void **data) +{ + JsonVerdictLogThread *vlt = SCCalloc(1, sizeof(JsonVerdictLogThread)); + if (unlikely(vlt == NULL)) + return TM_ECODE_FAILED; + + if (initdata == NULL) { + SCLogDebug("Error getting context for EvLogVerdict. \"initdata\" argument NULL"); + goto error_exit; + } + + /** Use the Output Context */ + vlt->verdict_ctx = ((OutputCtx *)initdata)->data; + vlt->ctx = CreateEveThreadCtx(t, vlt->verdict_ctx->eve_ctx); + if (!vlt->ctx) { + goto error_exit; + } + + *data = (void *)vlt; + return TM_ECODE_OK; + +error_exit: + SCFree(vlt); + return TM_ECODE_FAILED; +} + +static TmEcode JsonVerdictLogThreadDeInit(ThreadVars *t, void *data) +{ + JsonVerdictLogThread *vlt = (JsonVerdictLogThread *)data; + if (vlt == NULL) { + return TM_ECODE_OK; + } + + FreeEveThreadCtx(vlt->ctx); + + /* clear memory */ + memset(vlt, 0, sizeof(*vlt)); + + SCFree(vlt); + return TM_ECODE_OK; +} + +static void JsonVerdictOutputCtxFree(JsonVerdictOutputCtx *verdict_ctx) +{ + if (verdict_ctx != NULL) { + SCFree(verdict_ctx); + } +} + +static void JsonVerdictLogDeInitCtxSub(OutputCtx *output_ctx) +{ + SCLogDebug("cleaning up sub output_ctx %p", output_ctx); + JsonVerdictOutputCtx *verdict_ctx = (JsonVerdictOutputCtx *)output_ctx->data; + + if (verdict_ctx != NULL) { + SCFree(verdict_ctx); + } + SCFree(output_ctx); +} + +static OutputInitResult JsonVerdictLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + OutputInitResult result = { NULL, false }; + OutputJsonCtx *vlt = parent_ctx->data; + + JsonVerdictOutputCtx *verdict_ctx = SCCalloc(1, sizeof(*verdict_ctx)); + if (unlikely(verdict_ctx == NULL)) + return result; + + OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); + if (unlikely(output_ctx == NULL)) { + JsonVerdictOutputCtxFree(verdict_ctx); + return result; + } + memset(verdict_ctx, 0, sizeof(JsonVerdictOutputCtx)); + + verdict_ctx->eve_ctx = vlt; + + output_ctx->data = verdict_ctx; + output_ctx->DeInit = JsonVerdictLogDeInitCtxSub; + + result.ctx = output_ctx; + result.ok = true; + return result; +} + +void JsonVerdictLogRegister(void) +{ + OutputRegisterPacketSubModule(LOGGER_JSON_VERDICT, "eve-log", MODULE_NAME, "eve-log.verdict", + JsonVerdictLogInitCtxSub, JsonVerdictLogger, JsonVerdictLogCondition, + JsonVerdictLogThreadInit, JsonVerdictLogThreadDeInit, NULL); +} \ No newline at end of file diff --git a/src/output-json-verdict.h b/src/output-json-verdict.h new file mode 100644 index 000000000000..3668331cf425 --- /dev/null +++ b/src/output-json-verdict.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2023 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 Juliana Fajardini + * + */ + +#ifndef __OUTPUT_JSON_VERDICT_H__ +#define __OUTPUT_JSON_VERDICT_H__ + +void GetVerdictJsonInfo(JsonBuilder *jb, const Packet *p); +void JsonVerdictLogRegister(void); + +#endif /* __OUTPUT_JSON_VERDICT_H__ */ \ No newline at end of file diff --git a/src/output-json.c b/src/output-json.c index 95a6e5ef4c25..de176e632313 100644 --- a/src/output-json.c +++ b/src/output-json.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2021 Open Information Security Foundation +/* Copyright (C) 2007-2023 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 diff --git a/src/output.c b/src/output.c index c13ab4862eda..2f28bc299d51 100644 --- a/src/output.c +++ b/src/output.c @@ -46,6 +46,7 @@ #include "output-json.h" #include "output-json-alert.h" #include "output-json-anomaly.h" +#include "output-json-verdict.h" #include "output-json-flow.h" #include "output-json-netflow.h" #include "log-cf-common.h" @@ -1079,6 +1080,8 @@ void OutputRegisterLoggers(void) JsonAlertLogRegister(); JsonAnomalyLogRegister(); + /* Verdict JSON logger */ + JsonVerdictLogRegister(); /* flow/netflow */ JsonFlowLogRegister(); JsonNetFlowLogRegister(); diff --git a/src/suricata-common.h b/src/suricata-common.h index 47d578f72780..5b6181c68024 100644 --- a/src/suricata-common.h +++ b/src/suricata-common.h @@ -474,6 +474,7 @@ typedef enum { LOGGER_JSON_ALERT, LOGGER_JSON_ANOMALY, LOGGER_JSON_DROP, + LOGGER_JSON_VERDICT, LOGGER_FILE_STORE, LOGGER_JSON_FILE, LOGGER_TCP_DATA, diff --git a/suricata.yaml.in b/suricata.yaml.in index af7ad5344b72..6e27bf341e8d 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -168,6 +168,10 @@ outputs: # Enable the logging of tagged packets for rules using the # "tag" keyword. tagged-packets: yes + # Enable logging the final verdict for packets with the alert + verdict: yes + - verdict: + enabled: no # app layer frames - frame: # disabled by default as this is very verbose.