From dd14fdb9ce4e5b8f721e2f4056ad845c2b3add9d Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Mon, 20 Nov 2023 10:57:38 +0100 Subject: [PATCH] eve/alert: log payload directly from stream buffer This avoids looping over partly duplicate segments that cause output data corruption by logging parts of the stream data multiple times. For data with GAPs now add a indicator '[4 bytes missing]' similar to how Wireshark does it. Bug: #6553. --- src/output-json-alert.c | 95 ++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/output-json-alert.c b/src/output-json-alert.c index c3886231c001..bac57e390178 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -35,6 +35,7 @@ #include "tm-threads.h" #include "threadvars.h" #include "util-debug.h" +#include "stream-tcp.h" #include "util-logopenfile.h" #include "util-misc.h" @@ -126,17 +127,6 @@ typedef struct JsonAlertLogThread_ { OutputJsonThreadCtx *ctx; } JsonAlertLogThread; -/* Callback function to pack payload contents from a stream into a buffer - * so we can report them in JSON output. */ -static int AlertJsonDumpStreamSegmentCallback( - const Packet *p, TcpSegment *seg, void *data, const uint8_t *buf, uint32_t buflen) -{ - MemBuffer *payload = (MemBuffer *)data; - MemBufferWriteRaw(payload, buf, buflen); - - return 1; -} - static void AlertJsonTls(const Flow *f, JsonBuilder *js) { SSLState *ssl_state = (SSLState *)FlowGetAppState(f); @@ -717,9 +707,60 @@ void EveAddVerdict(JsonBuilder *jb, const Packet *p) jb_close(jb); } +struct AlertJsonStreamDataCallbackData { + MemBuffer *payload; + uint64_t last_re; +}; + +static int AlertJsonStreamDataCallback( + void *cb_data, const uint8_t *input, const uint32_t input_len, const uint64_t input_offset) +{ + struct AlertJsonStreamDataCallbackData *cbd = cb_data; + if (input_offset > cbd->last_re) { + char str[64]; + snprintf(str, sizeof(str), "[%" PRIu64 " bytes missing]", input_offset - cbd->last_re); + MemBufferWriteRaw(cbd->payload, str, strlen(str)); + } + + MemBufferWriteRaw(cbd->payload, input, input_len); + cbd->last_re = input_offset + input_len; + return 0; +} + +/** \internal + * \brief try to log stream data into payload/payload_printable + * \retval true stream data logged + * \retval false stream data not logged + */ +static bool AlertJsonStreamData(const AlertJsonOutputCtx *json_output_ctx, JsonAlertLogThread *aft, + Flow *f, const Packet *p, JsonBuilder *jb) +{ + TcpSession *ssn = f->protoctx; + TcpStream *stream = (PKT_IS_TOSERVER(p)) ? &ssn->client : &ssn->server; + + MemBufferReset(aft->payload_buffer); + struct AlertJsonStreamDataCallbackData cbd = { .payload = aft->payload_buffer }; + uint64_t unused = 0; + StreamReassembleLog(ssn, stream, AlertJsonStreamDataCallback, &cbd, 0, &unused, false); + if (cbd.payload->offset) { + if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) { + jb_set_base64(jb, "payload", cbd.payload->buffer, cbd.payload->offset); + } + + if (json_output_ctx->flags & LOG_JSON_PAYLOAD) { + uint8_t printable_buf[cbd.payload->offset + 1]; + uint32_t offset = 0; + PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer, + cbd.payload->offset); + jb_set_string(jb, "payload_printable", (char *)printable_buf); + } + return true; + } + return false; +} + static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) { - MemBuffer *payload = aft->payload_buffer; AlertJsonOutputCtx *json_output_ctx = aft->json_output_ctx; if (p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG)) @@ -828,33 +869,9 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) /* Is this a stream? If so, pack part of it into the payload field */ if (stream) { - uint8_t flag; - - MemBufferReset(payload); - - if (p->flowflags & FLOW_PKT_TOSERVER) { - flag = STREAM_DUMP_TOCLIENT; - } else { - flag = STREAM_DUMP_TOSERVER; - } - - StreamSegmentForEach((const Packet *)p, flag, - AlertJsonDumpStreamSegmentCallback, - (void *)payload); - if (payload->offset) { - if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) { - jb_set_base64(jb, "payload", payload->buffer, payload->offset); - } - - if (json_output_ctx->flags & LOG_JSON_PAYLOAD) { - uint8_t printable_buf[payload->offset + 1]; - uint32_t offset = 0; - PrintStringsToBuffer(printable_buf, &offset, - sizeof(printable_buf), - payload->buffer, payload->offset); - jb_set_string(jb, "payload_printable", (char *)printable_buf); - } - } else if (p->payload_len) { + const bool stream_data_logged = + AlertJsonStreamData(json_output_ctx, aft, p->flow, p, jb); + if (!stream_data_logged && p->payload_len) { /* Fallback on packet payload */ AlertAddPayload(json_output_ctx, jb, p); }