From: Alice Akaki Date: Wed, 9 Apr 2025 21:43:42 +0000 (-0400) Subject: detect: add email.received keyword X-Git-Tag: suricata-8.0.0-rc1~465 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bda08908343e57e7759e3b1840ab413a1aa4e53f;p=thirdparty%2Fsuricata.git detect: add email.received keyword email.received matches on MIME EMAIL Received This keyword maps to the EVE field email.received[] It is a sticky buffer Supports multiple buffer matching Supports prefiltering Ticket: #7599 --- diff --git a/doc/userguide/rules/email-keywords.rst b/doc/userguide/rules/email-keywords.rst index c7a480f5a3..206c3837e6 100644 --- a/doc/userguide/rules/email-keywords.rst +++ b/doc/userguide/rules/email-keywords.rst @@ -196,3 +196,29 @@ Example of a signature that would alert if an email contains the ``url`` ``test- .. container:: example-rule alert smtp any any -> any any (msg:"Test mime email url"; :example-rule-emphasis:`email.url; content:"test-site.org/blah/123/";` sid:1;) + +email.received +-------------- + +Matches ``Received`` field of an email. + +Comparison is case-sensitive. + +Syntax:: + + email.received; content:""; + +``email.received`` is a 'sticky buffer' and can be used as a ``fast_pattern``. + +``email.received`` supports multiple buffer matching, see :doc:`multi-buffer-matching`. + +This keyword maps to the EVE field ``email.received[]`` + +Example +^^^^^^^ + +Example of a signature that would alert if a packet contains the MIME field ``received`` with the value ``from [65.201.218.30] (helo=COZOXORY.club)by 173-66-46-112.wash.fios.verizon.net with esmtpa (Exim 4.86)(envelope-from )id 71cF63a9for mirjam@abrakadabra.ch; Mon, 29 Jul 2019 17:01:45 +0000`` + +.. container:: example-rule + + alert smtp any any -> any any (msg:"Test mime email received"; :example-rule-emphasis:`email.received; content:"from [65.201.218.30] (helo=COZOXORY.club)by 173-66-46-112.wash.fios.verizon.net with esmtpa (Exim 4.86)(envelope-from )id 71cF63a9for mirjam@abrakadabra.ch\; Mon, 29 Jul 2019 17:01:45 +0000";` sid:1;) diff --git a/doc/userguide/rules/multi-buffer-matching.rst b/doc/userguide/rules/multi-buffer-matching.rst index 63a143d766..5f894d222e 100644 --- a/doc/userguide/rules/multi-buffer-matching.rst +++ b/doc/userguide/rules/multi-buffer-matching.rst @@ -77,6 +77,7 @@ following keywords: * ``dns.answer.name`` * ``dns.query.name`` * ``dns.query`` +* ``email.received`` * ``email.url`` * ``file.data`` * ``file.magic`` diff --git a/rust/src/mime/detect.rs b/rust/src/mime/detect.rs index d178ccbc36..a9fbb631a7 100644 --- a/rust/src/mime/detect.rs +++ b/rust/src/mime/detect.rs @@ -60,3 +60,32 @@ pub unsafe extern "C" fn SCDetectMimeEmailGetUrl( return 0; } + +/// Intermediary function used in detect-email.c to access data from the MimeStateSMTP structure +/// for array header fields. +/// The hname parameter determines which data will be returned. +#[no_mangle] +pub unsafe extern "C" fn SCDetectMimeEmailGetDataArray( + ctx: &MimeStateSMTP, buffer: *mut *const u8, buffer_len: *mut u32, + hname: *const std::os::raw::c_char, idx: u32, +) -> u8 { + let c_str = CStr::from_ptr(hname); //unsafe + let str = c_str.to_str().unwrap_or(""); + + let mut i = 0; + for h in &ctx.headers[..ctx.main_headers_nb] { + if mime::slice_equals_lowercase(&h.name, str.as_bytes()) { + if i == idx { + *buffer = h.value.as_ptr(); + *buffer_len = h.value.len() as u32; + return 1; + } + i += 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} diff --git a/src/detect-email.c b/src/detect-email.c index 1c38a75602..385c166dd3 100644 --- a/src/detect-email.c +++ b/src/detect-email.c @@ -31,6 +31,7 @@ static int g_mime_email_date_buffer_id = 0; static int g_mime_email_message_id_buffer_id = 0; static int g_mime_email_x_mailer_buffer_id = 0; static int g_mime_email_url_buffer_id = 0; +static int g_mime_email_received_buffer_id = 0; static int DetectMimeEmailFromSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) { @@ -314,6 +315,46 @@ static InspectionBuffer *GetMimeEmailUrlData(DetectEngineThreadCtx *det_ctx, return buffer; } +static int DetectMimeEmailReceivedSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) +{ + if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_received_buffer_id) < 0) + return -1; + + if (DetectSignatureSetAppProto(s, ALPROTO_SMTP) < 0) + return -1; + + return 0; +} + +static InspectionBuffer *GetMimeEmailReceivedData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv, + const int list_id, uint32_t idx) +{ + InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx); + if (buffer == NULL || buffer->initialized) + return buffer; + + SMTPTransaction *tx = (SMTPTransaction *)txv; + + const uint8_t *b_email_received = NULL; + uint32_t b_email_received_len = 0; + + if (tx->mime_state == NULL) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + + if (SCDetectMimeEmailGetDataArray( + tx->mime_state, &b_email_received, &b_email_received_len, "received", idx) != 1) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + + InspectionBufferSetupMulti(det_ctx, buffer, transforms, b_email_received, b_email_received_len); + buffer->flags = DETECT_CI_FLAGS_SINGLE; + return buffer; +} + void DetectEmailRegister(void) { SCSigTableElmt kw = { 0 }; @@ -405,4 +446,15 @@ void DetectEmailRegister(void) DetectHelperMultiBufferMpmRegister("email.url", "MIME EMAIL URL", ALPROTO_SMTP, false, true, // to server GetMimeEmailUrlData); + + kw.name = "email.received"; + kw.desc = "'Received' field from an email"; + kw.url = "/rules/email-keywords.html#email.received"; + kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailReceivedSetup; + kw.flags = SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; + DetectHelperKeywordRegister(&kw); + g_mime_email_received_buffer_id = DetectHelperMultiBufferMpmRegister("email.received", + "MIME EMAIL RECEIVED", ALPROTO_SMTP, false, + true, // to server + GetMimeEmailReceivedData); }