From 52d6b7237eff85a1ec44186141bf6ddadbbfcccc Mon Sep 17 00:00:00 2001 From: Josh Rickard <10687261+MSAdministrator@users.noreply.github.com> Date: Tue, 19 May 2026 21:45:02 -0500 Subject: [PATCH 1/2] Create link_self_sender_cred_theft_external_domains.yml --- ...elf_sender_cred_theft_external_domains.yml | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 detection-rules/link_self_sender_cred_theft_external_domains.yml diff --git a/detection-rules/link_self_sender_cred_theft_external_domains.yml b/detection-rules/link_self_sender_cred_theft_external_domains.yml new file mode 100644 index 00000000000..2e3b0434c57 --- /dev/null +++ b/detection-rules/link_self_sender_cred_theft_external_domains.yml @@ -0,0 +1,42 @@ +name: "Link: Self-sender credential theft with external domains" +description: "Detects messages where the sender and recipient are the same address, containing external links and classified as credential theft by NLU analysis. The rule identifies self-sent messages from non-free email providers that pass DMARC authentication but contain 1-4 links to external domains, with machine learning classification indicating credential theft intent." +type: "rule" +severity: "high" +source: | + type.inbound + // self sender + and sender.email.email == recipients.to[0].email.email + // auth passes + and coalesce(headers.auth_summary.dmarc.pass, false) + // not free email + and sender.email.domain.root_domain not in $free_email_providers + // at least one link but less than 5 + and 0 < length(body.links) < 5 + and any(body.links, + .href_url.domain.root_domain != sender.email.domain.root_domain + ) + // A few fps with this pattern that approve data migration systems + and not any(filter(body.links, .href_url.path is not null), + strings.icontains(.href_url.path, '/consent/') + or strings.icontains(.href_url.path, '/approvals') + ) + and any(ml.nlu_classifier(body.current_thread.text).intents, + .name == "cred_theft" and .confidence != "low" + ) + // Not password reset, compromised account emails + and not ( + strings.icontains(body.current_thread.text, "compromised") + or strings.icontains(body.current_thread.text, "password") + ) + // under 2000 characters + and length(body.current_thread.text) < 2000 +attack_types: + - "Credential Phishing" +tactics_and_techniques: + - "Social engineering" +detection_methods: + - "Natural Language Understanding" + - "Sender analysis" + - "Header analysis" + - "URL analysis" + - "Content analysis" From 169789db1c9e893c64462d4fbdabb95e26e42692 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Wed, 20 May 2026 02:49:54 +0000 Subject: [PATCH 2/2] Auto-format MQL and add rule IDs --- .../link_self_sender_cred_theft_external_domains.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/detection-rules/link_self_sender_cred_theft_external_domains.yml b/detection-rules/link_self_sender_cred_theft_external_domains.yml index 2e3b0434c57..621316c7154 100644 --- a/detection-rules/link_self_sender_cred_theft_external_domains.yml +++ b/detection-rules/link_self_sender_cred_theft_external_domains.yml @@ -17,8 +17,8 @@ source: | ) // A few fps with this pattern that approve data migration systems and not any(filter(body.links, .href_url.path is not null), - strings.icontains(.href_url.path, '/consent/') - or strings.icontains(.href_url.path, '/approvals') + strings.icontains(.href_url.path, '/consent/') + or strings.icontains(.href_url.path, '/approvals') ) and any(ml.nlu_classifier(body.current_thread.text).intents, .name == "cred_theft" and .confidence != "low" @@ -40,3 +40,4 @@ detection_methods: - "Header analysis" - "URL analysis" - "Content analysis" +id: "ea16ad7e-a93a-51aa-ae13-76721e6cbca8"