Skip to content

Commit

Permalink
eve/alert: log payload directly from stream buffer
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
victorjulien committed Nov 21, 2023
1 parent b44a472 commit dd14fdb
Showing 1 changed file with 56 additions and 39 deletions.
95 changes: 56 additions & 39 deletions src/output-json-alert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit dd14fdb

Please sign in to comment.