Medium Severity

BEC/Fraud: Self-addressed reply with unrelated link in ongoing thread

Description

Flags messages where the sender appears to be replying to themselves within an established thread, but the first non-mailto link in the current message points to a domain unrelated to the sender and to any links seen in the previous thread history (accounting for Mimecast link wrapping). The rule confirms the sender was a genuine participant in prior thread messages and that no organizational recipients or domain matches exist in the earlier history, indicating a likely compromised external account being used to redirect the conversation via a newly introduced, unrelated link.

References

No references.

Sublime Security
Created Aug 26th, 2026 • Last updated Aug 26th, 2026
Source
type.inbound
// self sender behavior
and length(recipients.to) == 1
and length(recipients.cc) == 0
and sender.email.email == recipients.to[0].email.email

// the first link is not related to the sender
and filter(body.current_thread.links, .href_url.scheme != "mailto")[0].href_url.domain.root_domain != sender.email.domain.root_domain
// reply-to address (if present) is not related
and all(headers.reply_to,
        filter(body.current_thread.links, .href_url.scheme != "mailto")[0].href_url.domain.root_domain != .email.domain.root_domain
)
and length(body.previous_threads) > 1

// the first link's  domain does not appears in a previous thread's links
and not any(body.previous_threads,
            any(.links,
                // current link IS mimecast rewritten
                (
                  filter(body.current_thread.links,
                         .href_url.scheme != "mailto"
                  )[0].href_url.domain.root_domain in (
                    "mimecastprotect.com",
                    "mimecast.com"
                  )
                  and (
                    // the previous thread is also rewritten
                    strings.parse_domain(.href_url.query_params_decoded['domain'][0]
                    ).root_domain == strings.parse_domain(filter(body.current_thread.links,
                                                                 .href_url.scheme != "mailto"
                                                          )[0].href_url.query_params_decoded['domain'][0]
                    ).root_domain
                    // or the preiovus thread is not rewritten
                    or .href_url.domain.root_domain == strings.parse_domain(filter(body.current_thread.links,
                                                                                   .href_url.scheme != "mailto"
                                                                            )[0].href_url.query_params_decoded['domain'][0]
                    ).root_domain
                  )
                )
                // current link is NOT mimecast rewritten
                or (
                  // but the previous thread is mimecast rewritten
                  strings.parse_domain(.href_url.query_params_decoded['domain'][0]
                  ).root_domain == filter(body.current_thread.links,
                                          .href_url.scheme != "mailto"
                  )[0].href_url.domain.root_domain
                  // neither are mimecast rewritten
                  or .href_url.domain.root_domain == filter(body.current_thread.links,
                                                            .href_url.scheme != "mailto"
                  )[0].href_url.domain.root_domain
                )
            )
)

// none of the previous threads
and not any(body.previous_threads,
            // the subjet has been changed
            .subject.base =~ subject.base
            // none of the previous corrispondents are in the org_domains
            or any(flatten([.recipients.to, .recipients.bcc, .recipients.cc]),
                   .email.domain.domain in $org_domains
            )
            or .sender.email.domain.domain in $org_domains
)

// there is more than a single particiapte in the previous threads (by email and display_name)
and length(filter(flatten([
                            // pull all the sender emails from previous threads
                            map(body.previous_threads, .sender),
                            // get all the recipients from the previous threads
                            flatten(map(body.previous_threads, .recipients.to, )
                            )
                          ]
                  ),
                  // remove entries that have no email or display name
                  .email.email != ""
                  // remove enties that match the sender display name
                  and not strings.icontains(sender.display_name, .display_name)
                  // remove entires that match the sender email
                  and sender.email.email != .email.email
           // we are left with all the entries that are unrelated to the sender, there should be more than 0
           )
) > 0
MQL Rule Console
DocsLearning Labs

Playground

Test against your own EMLs or sample data.

Share

Post about this on your socials.

Get Started. Today.

Managed or self-managed. No MX changes.

Deploy and integrate a free Sublime instance in minutes.
Get Started