Description

Detects messages impersonating a Microsoft Teams invites by matching known invite text patterns while containing join links that do not resolve to Microsoft domains. Additional verification includes checking for absent phone dial-in options and missing standard Teams help text or HTML meeting components.

References

No references.

Sublime Security
Created May 5th, 2025 • Last updated Oct 9th, 2026
Source
type.inbound
and (
  (
    strings.icontains(body.current_thread.text, 'Microsoft Teams')
    and strings.icontains(body.current_thread.text,
                          'join the meeting',
                          'confirm your attendance',
                          'confirm attendance'
    )
    and strings.contains(body.current_thread.text, 'Meeting ID:')
    and strings.contains(body.current_thread.text, 'Passcode:')
  )
  or (
    strings.icontains(body.current_thread.text, "teams")
    // strings that give us confidence it's teams
    and 2 of (
      strings.icontains(body.current_thread.text, "internal"),
      strings.icontains(body.current_thread.text, "message"),
      strings.icontains(body.current_thread.text, "meeting"),
      strings.icontains(body.current_thread.text, "Download Teams")
    )
  )
  // either the subject or sender.display name containt Microsoft Teams and Meeting
  or (
    any([subject.base, sender.display_name],
        strings.icontains(., 'Microsoft Teams')
        and strings.icontains(., 'meeting')
    )
  )
  or length(filter(ml.nlu_classifier(body.current_thread.text).entities,
                   .name == "org"
                   and strings.icontains(.text, "microsoft teams")
            )
  ) >= 2
)
and (
  (
    // not a reply
    length(headers.references) == 0
    and headers.in_reply_to is null
    // few links
    and length(distinct(body.links, .href_url.url)) < 10
    // short body
    and (
      length(body.current_thread.text) < 600
      // account for NLU disclaimer in length
      or any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
                        .name == "disclaimer"
                 ),
                 .text
             ),
             (length(body.current_thread.text) - length(.)) < 600
      )
      // NLU only tags the first english disclaimer, so pick up repeat disclaimers or non-english disclaimers as well
      or any(regex.iextract(body.current_thread.text,
                            '^(?P<disclaimer>.{100,}?)(?:confidentiality notice|nota di riservatezza)'
             ),
             length(.named_groups["disclaimer"]) < 600
      )
    )
  ) // or sender domain is new
  or network.whois(sender.email.domain).days_old < 365
)
// no unsubscribe links
// common in newsletters which link to a webinar style event
and not any(body.links, strings.icontains(.display_text, "unsub"))

// one of the links contains is a CTA that doesn't link to MS
and any(body.current_thread.links,
        (
          .display_text =~ "join the meeting"
          or strings.icontains(.display_text, "join the meeting")
          or strings.icontains(.display_text, "play recording")
          // is a mismatched domain via .display_url
          or (
            .display_url.domain.root_domain in (
              "microsoft.com",
              "microsoft.us",
              "microsoft.cn",
              "live.com"
            )
            and .mismatched
          )
        )
        and .href_url.domain.root_domain not in (
          "microsoft.com",
          "microsoft.us",
          "microsoft.cn",
          "live.com"
        )
        and not (
          .href_url.domain.root_domain == "mimecastprotect.com"
          and (
            strings.parse_domain(.href_url.query_params_decoded["domain"][0]).root_domain in (
              "microsoft.com",
              "microsoft.us",
              "microsoft.cn",
              "live.com"
            )
            or strings.parse_domain(.href_url.query_params_decoded["domain"][0]).root_domain in $bulk_mailer_url_root_domains
          )
        )
        // rewriters often abstract the link
        and .href_url.domain.root_domain not in $bulk_mailer_url_root_domains
)
// missing the dial by phone element
and not strings.icontains(body.current_thread.text, 'Dial in by phone')

// any of these suspicious elements from the body
and (
  // malicious samples leveraged recipient domain branding here
  not strings.icontains(body.current_thread.text, 'Microsoft Teams Need help?')
  // malicious samples contained unique html elements not present in legit ones
  or strings.icontains(body.html.raw, '<div class="meeting-title">')
  or strings.icontains(body.html.raw, '<div class="meeting-time">')
  or strings.icontains(body.html.raw, '<div class="meeting-location">')
  or strings.icontains(body.html.raw, '<span class="conflict-badge">')
  or strings.icontains(body.html.raw, 'class="join-button"')
)

// negate highly trusted sender domains unless they fail DMARC authentication
and not (
  sender.email.domain.root_domain in $high_trust_sender_root_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)
MQL Rule Console
•Docs•Learning 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