Detection rules › Sublime MQL

Brand impersonation: Microsoft Planner with suspicious link

Severity
medium
Type
rule
Source
github.com/sublime-security/sublime-rules

Impersonation of Microsoft Planner, a component of the Microsoft 365 software suite.

Threat classification

Sublime's own taxonomy (not MITRE ATT&CK).

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Image as content, Impersonation: Brand, Social engineering

Event coverage

Rule body MQL

type.inbound
// suspicious link
and any(body.links,
        (
          .href_url.domain.root_domain not in $tranco_1m
          or .href_url.domain.domain in $free_file_hosts
          or .href_url.domain.root_domain in $free_file_hosts
          or .href_url.domain.root_domain in $free_subdomain_hosts
          or .href_url.domain.domain in $url_shorteners
          or .href_url.domain.domain in $social_landing_hosts
          or .href_url.domain.root_domain in $social_landing_hosts
          or 

          // mass mailer link, masks the actual URL
          .href_url.domain.root_domain in (
            "hubspotlinks.com",
            "mandrillapp.com",
            "sendgrid.net",
            "rs6.net"
          )

          // Google AMP redirect
          or (
            .href_url.domain.sld == "google"
            and strings.starts_with(.href_url.path, "/amp/")
          )

          // Recipient email address in link
          or any(body.links,
                 any(recipients.to,
                     strings.icontains(..href_url.url, .email.email)
                     and any(recipients.to, .email.domain.valid)
                 )
          )
          or .href_url.domain.root_domain == "beehiiv.com"
        )

        // exclude sources of potential FPs
        and (
          .href_url.domain.root_domain not in (
            "svc.ms",
            "sharepoint.com",
            "1drv.ms",
            "microsoft.com",
            "aka.ms",
            "msftauthimages.net",
            "mimecastprotect.com",
            "office.com",
            "microsoftproject.com"
          )
          or any(body.links, .href_url.domain.domain in $free_file_hosts)
        )
        and .href_url.domain.root_domain not in $org_domains
        and .href_url.domain.valid
        and regex.icontains(.display_text,
                            "(?:go.?to|view|show|display|access|open.?in|review.?on) (teams?|planner|group|task|intranet|discussions?)"
        )
)

// not a reply
and (length(headers.references) == 0 or headers.in_reply_to is null)

// Planner logo
// LogoDetect coming soon
and (
  all(attachments,
      .file_type in $file_types_images
      and any(file.explode(.),
              // small, relatively square image
              (
                .scan.exiftool.image_height / .scan.exiftool.image_width
              ) > 0.9
              and (.scan.exiftool.image_height + .scan.exiftool.image_width) < 500
      )
  )
)

// suspicious content
and (
  2 of (
    strings.ilike(body.current_thread.text, "*assigned*new team*"),
    strings.ilike(body.current_thread.text, "*Microsoft Office 365*"),
    strings.ilike(body.current_thread.text, "*internal planner*"),
    strings.ilike(body.current_thread.text, "*internal task*"),
    any(recipients.to,
        strings.icontains(body.current_thread.text, .email.domain.sld)
    )
  )
  or (
    any(ml.nlu_classifier(body.current_thread.text).intents,
        .name == "cred_theft" and .confidence in~ ("medium", "high")
    )
  )
  // multiple links, but all the same root domain
  or (
    length(distinct(body.links, .href_url.domain.root_domain)) == 1
    and 2 < length(body.links) < 10
    and all(body.links,
            .href_url.domain.root_domain != sender.email.domain.root_domain
    )
  )
)
and sender.email.domain.root_domain not in (
  "bing.com",
  "microsoft.com",
  "microsoftonline.com",
  "microsoftproject.com",
  "microsoftstoreemail.com",
  "microsoftsupport.com",
  "microsoft365.com",
  "office.com",
  "office365.com",
  "onedrive.com",
  "sharepointonline.com",
  "yammer.com",
)

// 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().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)
and not profile.by_sender().any_messages_benign

// exclude marketing jargon from ms partners
and not regex.icontains(body.current_thread.text,
                        '(schedul(e|ing)|set up).{0,20}(call|meeting|demo|zoom|conversation|time|tool|discussion)|book.{0,10}(meeting|demo|call|slot|time)|connect.{0,12}(with me|phone|email)|my.{0,10}(calendar|cal)|reserve.{0,10}s[pl]ot|break the ice|want to know more?|miss your chance|if you no longer wish|if you no longer want|if you wish to opt out|low-code (development|approach|solution|journey|platform)|invite.{0,30}(webinar|presentation)'
)

Detection logic

Scope: inbound message.

Impersonation of Microsoft Planner, a component of the Microsoft 365 software suite.

  1. inbound message
  2. any of body.links where all hold:
    • any of:
      • .href_url.domain.root_domain not in $tranco_1m
      • .href_url.domain.domain in $free_file_hosts
      • .href_url.domain.root_domain in $free_file_hosts
      • .href_url.domain.root_domain in $free_subdomain_hosts
      • .href_url.domain.domain in $url_shorteners
      • .href_url.domain.domain in $social_landing_hosts
      • .href_url.domain.root_domain in $social_landing_hosts
      • .href_url.domain.root_domain in ('hubspotlinks.com', 'mandrillapp.com', 'sendgrid.net', 'rs6.net')
      • all of:
        • .href_url.domain.sld is 'google'
        • .href_url.path starts with '/amp/'
      • any of body.links where:
        • any of recipients.to where all hold:
          • strings.icontains(.href_url.url)
          • any of recipients.to where:
            • .email.domain.valid
      • .href_url.domain.root_domain is 'beehiiv.com'
    • any of:
      • .href_url.domain.root_domain not in ('svc.ms', 'sharepoint.com', '1drv.ms', 'microsoft.com', 'aka.ms', 'msftauthimages.net', 'mimecastprotect.com', 'office.com', 'microsoftproject.com')
      • any of body.links where:
        • .href_url.domain.domain in $free_file_hosts
    • .href_url.domain.root_domain not in $org_domains
    • .href_url.domain.valid
    • .display_text matches '(?:go.?to|view|show|display|access|open.?in|review.?on) (teams?|planner|group|task|intranet|discussions?)'
  3. any of:
    • length(headers.references) is 0
    • headers.in_reply_to is missing
  4. all of attachments where all hold:
    • .file_type in $file_types_images
    • any of file.explode(.) where all hold:
      • .scan.exiftool.image_height / .scan.exiftool.image_width > 0.9
      • .scan.exiftool.image_height + .scan.exiftool.image_width < 500
  5. any of:
    • at least 2 of:
      • body.current_thread.text matches '*assigned*new team*'
      • body.current_thread.text matches '*Microsoft Office 365*'
      • body.current_thread.text matches '*internal planner*'
      • body.current_thread.text matches '*internal task*'
      • any of recipients.to where:
        • strings.icontains(body.current_thread.text)
    • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
      • .name is 'cred_theft'
      • .confidence in ('medium', 'high')
    • all of:
      • length(distinct(body.links, .href_url.domain.root_domain)) is 1
      • all of:
        • length(body.links) > 2
        • length(body.links) < 10
      • all of body.links where:
        • .href_url.domain.root_domain is not sender.email.domain.root_domain
  6. sender.email.domain.root_domain not in ('bing.com', 'microsoft.com', 'microsoftonline.com', 'microsoftproject.com', 'microsoftstoreemail.com', 'microsoftsupport.com', 'microsoft365.com', 'office.com', 'office365.com', 'onedrive.com', 'sharepointonline.com', 'yammer.com')
  7. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains
  8. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  9. not:
    • profile.by_sender().any_messages_benign
  10. not:
    • body.current_thread.text matches '(schedul(e|ing)|set up).{0,20}(call|meeting|demo|zoom|conversation|time|tool|discussion)|book.{0,10}(meeting|demo|call|slot|time)|connect.{0,12}(with me|phone|email)|my.{0,10}(calendar|cal)|reserve.{0,10}s[pl]ot|break the ice|want to know more?|miss your chance|if you no longer wish|if you no longer want|if you wish to opt out|low-code (development|approach|solution|journey|platform)|invite.{0,30}(webinar|presentation)'

Inspects: attachments[].file_type, body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, body.links[].href_url.domain.sld, body.links[].href_url.domain.valid, body.links[].href_url.path, body.links[].href_url.url, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, recipients.to, recipients.to[].email.domain.sld, recipients.to[].email.domain.valid, recipients.to[].email.email, sender.email.domain.root_domain, type.inbound. Sensors: file.explode, ml.nlu_classifier, profile.by_sender, regex.icontains, strings.icontains, strings.ilike, strings.starts_with. Reference lists: $file_types_images, $free_file_hosts, $free_subdomain_hosts, $high_trust_sender_root_domains, $org_domains, $social_landing_hosts, $tranco_1m, $url_shorteners.

Indicators matched (37)

FieldMatchValue
body.links[].href_url.domain.root_domainmemberhubspotlinks.com
body.links[].href_url.domain.root_domainmembermandrillapp.com
body.links[].href_url.domain.root_domainmembersendgrid.net
body.links[].href_url.domain.root_domainmemberrs6.net
body.links[].href_url.domain.sldequalsgoogle
strings.starts_withprefix/amp/
body.links[].href_url.domain.root_domainequalsbeehiiv.com
body.links[].href_url.domain.root_domainmembersvc.ms
body.links[].href_url.domain.root_domainmembersharepoint.com
body.links[].href_url.domain.root_domainmember1drv.ms
body.links[].href_url.domain.root_domainmembermicrosoft.com
body.links[].href_url.domain.root_domainmemberaka.ms
25 more
body.links[].href_url.domain.root_domainmembermsftauthimages.net
body.links[].href_url.domain.root_domainmembermimecastprotect.com
body.links[].href_url.domain.root_domainmemberoffice.com
body.links[].href_url.domain.root_domainmembermicrosoftproject.com
regex.icontainsregex(?:go.?to|view|show|display|access|open.?in|review.?on) (teams?|planner|group|task|intranet|discussions?)
strings.ilikesubstring*assigned*new team*
strings.ilikesubstring*Microsoft Office 365*
strings.ilikesubstring*internal planner*
strings.ilikesubstring*internal task*
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscred_theft
ml.nlu_classifier(body.current_thread.text).intents[].confidencemembermedium
ml.nlu_classifier(body.current_thread.text).intents[].confidencememberhigh
sender.email.domain.root_domainmemberbing.com
sender.email.domain.root_domainmembermicrosoft.com
sender.email.domain.root_domainmembermicrosoftonline.com
sender.email.domain.root_domainmembermicrosoftproject.com
sender.email.domain.root_domainmembermicrosoftstoreemail.com
sender.email.domain.root_domainmembermicrosoftsupport.com
sender.email.domain.root_domainmembermicrosoft365.com
sender.email.domain.root_domainmemberoffice.com
sender.email.domain.root_domainmemberoffice365.com
sender.email.domain.root_domainmemberonedrive.com
sender.email.domain.root_domainmembersharepointonline.com
sender.email.domain.root_domainmemberyammer.com
regex.icontainsregex(schedul(e|ing)|set up).{0,20}(call|meeting|demo|zoom|conversation|time|tool|discussion)|book.{0,10}(meeting|demo|call|slot|time)|connect.{0,12}(with me|phone|email)|my.{0,10}(calendar|cal)|reserve.{0,10}s[pl]ot|break the ice|want to know more?|miss your chance|if you no longer wish|if you no longer want|if you wish to opt out|low-code (development|approach|solution|journey|platform)|invite.{0,30}(webinar|presentation)