From 5746df5859d26f86534a42e21d7ae8ce65ec7c02 Mon Sep 17 00:00:00 2001 From: Brandon Murphy <4827852+zoomequipd@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:23:21 -0500 Subject: [PATCH 1/3] Create Impersonation_vip_fake_thread_not_recip.yml --- ...mpersonation_vip_fake_thread_not_recip.yml | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 detection-rules/Impersonation_vip_fake_thread_not_recip.yml diff --git a/detection-rules/Impersonation_vip_fake_thread_not_recip.yml b/detection-rules/Impersonation_vip_fake_thread_not_recip.yml new file mode 100644 index 00000000000..ba2dcd8902f --- /dev/null +++ b/detection-rules/Impersonation_vip_fake_thread_not_recip.yml @@ -0,0 +1,181 @@ +name: "VIP Impersonation: Fake Thread with VIP Author not in Recipients from a New Sender" +description: "This rule is intended to detect fake threads that are impersonating a VIP. It looks for a previous thread matching $org_vips display name and email address and checks the VIP authoring the previous thread is not a current recipient and this message is from a suspicous sender" +type: "rule" +severity: "medium" +source: | + type.inbound + and any([body.current_thread.text, body.html.display_text, body.plain.raw], + // find threaded messages with a _few_ previous messages, but not too many + 3 <= sum([ + strings.icount(., "from:"), + strings.icount(., "to:"), + strings.icount(., "sent:"), + strings.icount(., "date:"), + strings.icount(., "cc:"), + strings.icount(., "subject:") + ] + ) < 60 + ) + // There is a previous thread which has been authored by a VIP + and any($org_vips, + ( + // without quotes around display_name + strings.icontains(body.html.display_text, + strings.concat("From: ", + .display_name, + " <", + .email, + ">" + ) + ) + // with single quotes around display_name + or strings.icontains(body.html.display_text, + strings.concat("From: \'", + .display_name, + "\' <", + .email, + ">" + ) + ) + // with quotes around display_name + or strings.icontains(body.html.display_text, + strings.concat("From: \"", + .display_name, + "\" <", + .email, + ">" + ) + ) + // with quotes, with mailto + or strings.icontains(body.html.display_text, + strings.concat("From: \"", + .display_name, + "\" [mailto:", + .email, + "]" + ) + ) + // without quotes, with mailto + or strings.icontains(body.html.display_text, + strings.concat("From: ", + .display_name, + " [mailto:", + .email, + "]" + ) + ) + // without quotes only email address + or strings.icontains(body.html.display_text, + strings.concat("From: ", .email) + ) + // with quotes only email address + or strings.icontains(body.html.display_text, + strings.concat("From: \"", .email) + ) + ) + ) + // and none of the recipients that authored a previous thread are in $org_vips + and not any([recipients.to, recipients.cc, recipients.bcc], + any(., // the recipient shows up as a previous sender in the thread + ( + // without quotes around display_name + strings.icontains(body.html.display_text, + strings.concat("From: ", + .display_name, + " <", + .email.email, + ">" + ) + ) + // with single quotes around display_name + or strings.icontains(body.html.display_text, + strings.concat("From: \'", + .display_name, + "\' <", + .email.email, + ">" + ) + ) + // with quotes around display_name + or strings.icontains(body.html.display_text, + strings.concat("From: \"", + .display_name, + "\" <", + .email.email, + ">" + ) + ) + // with quotes, with mailto + or strings.icontains(body.html.display_text, + strings.concat("From: \"", + .display_name, + "\" [mailto:", + .email.email, + "]" + ) + ) + // without quotes, with mailto + or strings.icontains(body.html.display_text, + strings.concat("From: ", + .display_name, + " [mailto:", + .email.email, + "]" + ) + ) + // without quotes only email address + or strings.icontains(body.html.display_text, + strings.concat("From: ", .email.email) + ) + // with quotes only email address + or strings.icontains(body.html.display_text, + strings.concat("From: \"", .email.email) + ) + // without quotes only display_name + or strings.icontains(body.html.display_text, + strings.concat("From: ", .display_name) + ) + // with quotes only display_namea + or strings.icontains(body.html.display_text, + strings.concat("From: \"", .display_name) + ) + ) + // and that recipeint is in the $org_vips + and any($org_vips, ..email.email == .email) + ) + ) + and ( + profile.by_sender().prevalence in ("new", "rare") + or not profile.by_sender().solicited + or profile.by_sender().days_known < 10 + ) + // negate org domains unless they fail DMARC authentication + and ( + ( + sender.email.domain.root_domain in $org_domains + and not headers.auth_summary.dmarc.pass + ) + or sender.email.domain.root_domain not in $org_domains + ) + + // negate highly trusted sender domains unless they fail DMARC authentication + and ( + ( + sender.email.domain.root_domain in $high_trust_sender_root_domains + and not headers.auth_summary.dmarc.pass + ) + or sender.email.domain.root_domain not in $high_trust_sender_root_domains + ) + + and not profile.by_sender().any_false_positives + +attack_types: + - "BEC/Fraud" +tactics_and_techniques: + - "Evasion" + - "Impersonation: VIP" + - "Social engineering" + - "Spoofing" +detection_methods: + - "Content analysis" + - "Sender analysis" From 8e2f40ac1edf3eb261fb6d335b48937c37d9fdc0 Mon Sep 17 00:00:00 2001 From: ID Generator Date: Fri, 20 Sep 2024 20:27:08 +0000 Subject: [PATCH 2/3] Auto add rule ID --- detection-rules/Impersonation_vip_fake_thread_not_recip.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/detection-rules/Impersonation_vip_fake_thread_not_recip.yml b/detection-rules/Impersonation_vip_fake_thread_not_recip.yml index ba2dcd8902f..6a6444ddf11 100644 --- a/detection-rules/Impersonation_vip_fake_thread_not_recip.yml +++ b/detection-rules/Impersonation_vip_fake_thread_not_recip.yml @@ -179,3 +179,4 @@ tactics_and_techniques: detection_methods: - "Content analysis" - "Sender analysis" +id: "36af5488-d10e-5b76-adaf-e0d0a0955aaa" From e9aa5006ad26ecc14ac6cbf2df432ba8fc59b6a1 Mon Sep 17 00:00:00 2001 From: Brandon Murphy <4827852+zoomequipd@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:20:26 -0500 Subject: [PATCH 3/3] Update Impersonation_vip_fake_thread_not_recip.yml better handle non-html body emails --- ...mpersonation_vip_fake_thread_not_recip.yml | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/detection-rules/Impersonation_vip_fake_thread_not_recip.yml b/detection-rules/Impersonation_vip_fake_thread_not_recip.yml index 6a6444ddf11..5e6a5f84510 100644 --- a/detection-rules/Impersonation_vip_fake_thread_not_recip.yml +++ b/detection-rules/Impersonation_vip_fake_thread_not_recip.yml @@ -20,7 +20,7 @@ source: | and any($org_vips, ( // without quotes around display_name - strings.icontains(body.html.display_text, + strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .display_name, " <", @@ -29,7 +29,7 @@ source: | ) ) // with single quotes around display_name - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \'", .display_name, "\' <", @@ -38,7 +38,7 @@ source: | ) ) // with quotes around display_name - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .display_name, "\" <", @@ -47,7 +47,7 @@ source: | ) ) // with quotes, with mailto - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .display_name, "\" [mailto:", @@ -56,7 +56,7 @@ source: | ) ) // without quotes, with mailto - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .display_name, " [mailto:", @@ -65,11 +65,11 @@ source: | ) ) // without quotes only email address - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .email) ) // with quotes only email address - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .email) ) ) @@ -79,7 +79,7 @@ source: | any(., // the recipient shows up as a previous sender in the thread ( // without quotes around display_name - strings.icontains(body.html.display_text, + strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .display_name, " <", @@ -88,7 +88,7 @@ source: | ) ) // with single quotes around display_name - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \'", .display_name, "\' <", @@ -97,7 +97,7 @@ source: | ) ) // with quotes around display_name - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .display_name, "\" <", @@ -106,7 +106,7 @@ source: | ) ) // with quotes, with mailto - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .display_name, "\" [mailto:", @@ -115,7 +115,7 @@ source: | ) ) // without quotes, with mailto - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .display_name, " [mailto:", @@ -124,19 +124,19 @@ source: | ) ) // without quotes only email address - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .email.email) ) // with quotes only email address - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .email.email) ) // without quotes only display_name - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: ", .display_name) ) // with quotes only display_namea - or strings.icontains(body.html.display_text, + or strings.icontains(coalesce(body.html.display_text, body.plain.raw), strings.concat("From: \"", .display_name) ) ) @@ -168,7 +168,6 @@ source: | ) and not profile.by_sender().any_false_positives - attack_types: - "BEC/Fraud" tactics_and_techniques: