Brand impersonation: Microsoft Teams invitation

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.

Sublime rule (View on GitHub)

  1name: "Brand impersonation: Microsoft Teams invitation"
  2description: "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."
  3type: "rule"
  4severity: "high"
  5source: |
  6  type.inbound
  7  and (
  8    (
  9      strings.icontains(body.current_thread.text, 'Microsoft Teams')
 10      and strings.icontains(body.current_thread.text,
 11                            'join the meeting',
 12                            'confirm your attendance',
 13                            'confirm attendance'
 14      )
 15      and strings.contains(body.current_thread.text, 'Meeting ID:')
 16      and strings.contains(body.current_thread.text, 'Passcode:')
 17    )
 18    or (
 19      strings.icontains(body.current_thread.text, "teams")
 20      // strings that give us confidence it's teams
 21      and 2 of (
 22        strings.icontains(body.current_thread.text, "internal"),
 23        strings.icontains(body.current_thread.text, "message"),
 24        strings.icontains(body.current_thread.text, "meeting"),
 25        strings.icontains(body.current_thread.text, "Download Teams")
 26      )
 27    )
 28    // either the subject or sender.display name containt Microsoft Teams and Meeting
 29    or (
 30      any([subject.base, sender.display_name],
 31          strings.icontains(., 'Microsoft Teams')
 32          and strings.icontains(., 'meeting')
 33      )
 34    )
 35  )
 36  // not a reply
 37  and length(headers.references) == 0
 38  and headers.in_reply_to is null
 39  // few links
 40  and length(distinct(body.links, .href_url.url)) < 10
 41  // short body
 42  and length(body.current_thread.text) < 600
 43  // no unsubscribe links
 44  // common in newsletters which link to a webinar style event
 45  and not any(body.links, strings.icontains(.display_text, "unsub"))
 46  
 47  // one of the links contains is a CTA that doesn't link to MS
 48  and any(body.current_thread.links,
 49          (
 50            .display_text =~ "join the meeting"
 51            or strings.icontains(.display_text, "join the meeting")
 52            or strings.icontains(.display_text, "play recording")
 53            // is a mismatched domain via .display_url
 54            or (
 55              .display_url.domain.root_domain in (
 56                "microsoft.com",
 57                "microsoft.us",
 58                "microsoft.cn",
 59                "live.com"
 60              )
 61              and .mismatched
 62            )
 63          )
 64          and .href_url.domain.root_domain not in (
 65            "microsoft.com",
 66            "microsoft.us",
 67            "microsoft.cn",
 68            "live.com"
 69          )
 70          and not (
 71            .href_url.domain.root_domain == "mimecastprotect.com"
 72            and (
 73              strings.parse_domain(.href_url.query_params_decoded["domain"][0]).root_domain in (
 74                "microsoft.com",
 75                "microsoft.us",
 76                "microsoft.cn",
 77                "live.com"
 78              )
 79              or strings.parse_domain(.href_url.query_params_decoded["domain"][0]).root_domain in $bulk_mailer_url_root_domains
 80            )
 81          )
 82          // rewriters often abstract the link
 83          and .href_url.domain.root_domain not in $bulk_mailer_url_root_domains
 84  )
 85  // missing the dial by phone element
 86  and not strings.icontains(body.current_thread.text, 'Dial in by phone')
 87  
 88  // any of these suspicious elements from the body
 89  and (
 90    // malicious samples leveraged recipient domain branding here
 91    not strings.icontains(body.current_thread.text, 'Microsoft Teams Need help?')
 92    // malicious samples contained unique html elements not present in legit ones
 93    or strings.icontains(body.html.raw, '<div class="meeting-title">')
 94    or strings.icontains(body.html.raw, '<div class="meeting-time">')
 95    or strings.icontains(body.html.raw, '<div class="meeting-location">')
 96    or strings.icontains(body.html.raw, '<span class="conflict-badge">')
 97    or strings.icontains(body.html.raw, 'class="join-button"')
 98  )
 99  
100  // negate highly trusted sender domains unless they fail DMARC authentication
101  and (
102    (
103      sender.email.domain.root_domain in $high_trust_sender_root_domains
104      and not headers.auth_summary.dmarc.pass
105    )
106    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
107  )  
108attack_types:
109  - "Credential Phishing"
110tactics_and_techniques:
111  - "Impersonation: Brand"
112  - "Social engineering"
113detection_methods:
114  - "Content analysis"
115  - "Header analysis"
116  - "HTML analysis"
117  - "URL analysis"
118id: "46410ad8-3465-505f-a78e-f77704910a91"
to-top