Detection rules › Sublime MQL

Brand impersonation: Microsoft with embedded logo and credential theft language

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

This rule detects messages impersonating Microsoft via a logo and contains credential theft language. From a new and unsolicited sender.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesImpersonation: Brand, Social engineering

Event coverage

Rule body MQL

type.inbound
and (
  (
    length(attachments) > 0
    and all(attachments,
            .file_type in $file_types_images or .file_type == "pdf"
    )
  )
  or length(attachments) == 0
)
and any(ml.logo_detect(file.message_screenshot()).brands,
        strings.starts_with(.name, "Microsoft")
)
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name == "cred_theft" and .confidence in ("medium", "high")
  )
  or (
    length(body.current_thread.text) == 0
    //
    // This rule makes use of a beta feature and is subject to change without notice
    // using the beta feature in custom rules is not suggested until it has been formally released
    //
    and any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents,
            .name == "cred_theft" and .confidence in ("medium", "high")
    )
  )
)
and (
  not (
    headers.auth_summary.dmarc.pass
    and headers.auth_summary.dmarc.details.from.domain in (
      "azureadnotifications.us",
      "microsoft.com",
      "sharepointonline.com",
      "cloudappsecurity.com",
      "microsoftsupport.com",
      "microsoft.onmicrosoft.com",
      "yammer.com"
    )
  )
  or headers.auth_summary.dmarc.pass is null
  or headers.auth_summary.dmarc.details.from.domain is null
)
and not (
  sender.email.domain.domain == "planner.office365.com"
  and headers.return_path.email == "noreply@planner.office365.com"
  and headers.auth_summary.dmarc.details.from.root_domain == "office365.com"
)
// Microsoft has some legit onmicrosoft domains...
and not (
  sender.email.domain.domain == "microsoft.onmicrosoft.com"
  and headers.auth_summary.spf.pass
  and all(body.links, .href_url.domain.root_domain == "microsoft.com")
)
// message is not from sharepoint actual (additional check in case DMARC check above fails to bail out)
and not (
  (
    strings.ilike(headers.message_id,
                  '<Share-*',
                  '<MassDelete-*',
                  '<FileDeleteAfterExpiration-*',
                  '<NotifyOwnerSharedWithExternalUsers*',
                  '<OneTimePasscode*'
    )
    and strings.ends_with(headers.message_id, '@odspnotify>')
  )
  or (
    any(headers.hops,
        any(.fields,
            .name == "X-Google-Original-Message-ID"
            and strings.ilike(.value,
                              '<Share-*',
                              '<MassDelete-*',
                              '<FileDeleteAfterExpiration-*',
                              '<NotifyOwnerSharedWithExternalUsers*',
                              '<OneTimePasscode*'
            )
            and strings.ends_with(.value, '@odspnotify>')
        )
    )
  )
)
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

// negate org domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $org_domains
    and (
      not headers.auth_summary.dmarc.pass
      // MS quarantine digest emails from an org domain are router "internally" to MS, therefore, there is no authentication information
      or not (
        headers.auth_summary.dmarc.pass is null
        and all(headers.domains,
                .root_domain in ("outlook.com", "office365.com")
        )
        // typical emails from freemail Outlook accounts are from prod.outlook.com
        and strings.ends_with(headers.message_id, "protection.outlook.com>")
      )
    )
  )
  or sender.email.domain.root_domain not in $org_domains
)

// negate sharepoint file shares with mimecast rewrites
and not (
  // rewritten message ID
  strings.iends_with(headers.message_id, 'mimecast.lan>')
  and all(filter(body.links,
                 strings.icontains(subject.subject, .display_text)
                 or .display_text == "Open"
          ),
          .href_url.domain.root_domain in (
            "mimecastprotect.com",
            "mimecast.com"
          )
          and any(.href_url.query_params_decoded["domain"],
                  strings.parse_domain(.).tld == "ms"
                  or strings.parse_domain(.).root_domain == "sharepoint.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().any_messages_benign

Detection logic

Scope: inbound message.

This rule detects messages impersonating Microsoft via a logo and contains credential theft language. From a new and unsolicited sender.

  1. inbound message
  2. any of:
    • all of:
      • length(attachments) > 0
      • all of attachments where any holds:
        • .file_type in $file_types_images
        • .file_type is 'pdf'
    • length(attachments) is 0
  3. any of ml.logo_detect(file.message_screenshot()).brands where:
    • .name starts with 'Microsoft'
  4. any of:
    • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
      • .name is 'cred_theft'
      • .confidence in ('medium', 'high')
    • all of:
      • length(body.current_thread.text) is 0
      • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents where all hold:
        • .name is 'cred_theft'
        • .confidence in ('medium', 'high')
  5. any of:
    • not:
      • all of:
        • headers.auth_summary.dmarc.pass
        • headers.auth_summary.dmarc.details.from.domain in ('azureadnotifications.us', 'microsoft.com', 'sharepointonline.com', 'cloudappsecurity.com', 'microsoftsupport.com', 'microsoft.onmicrosoft.com', 'yammer.com')
    • headers.auth_summary.dmarc.pass is missing
    • headers.auth_summary.dmarc.details.from.domain is missing
  6. not:
    • all of:
      • sender.email.domain.domain is 'planner.office365.com'
      • headers.return_path.email is 'noreply@planner.office365.com'
      • headers.auth_summary.dmarc.details.from.root_domain is 'office365.com'
  7. not:
    • all of:
      • sender.email.domain.domain is 'microsoft.onmicrosoft.com'
      • headers.auth_summary.spf.pass
      • all of body.links where:
        • .href_url.domain.root_domain is 'microsoft.com'
  8. none of:
    • all of:
      • headers.message_id matches any of 5 patterns
        • <Share-*
        • <MassDelete-*
        • <FileDeleteAfterExpiration-*
        • <NotifyOwnerSharedWithExternalUsers*
        • <OneTimePasscode*
      • headers.message_id ends with '@odspnotify>'
    • any of headers.hops where:
      • any of .fields where all hold:
        • .name is 'X-Google-Original-Message-ID'
        • .value matches any of 5 patterns
          • <Share-*
          • <MassDelete-*
          • <FileDeleteAfterExpiration-*
          • <NotifyOwnerSharedWithExternalUsers*
          • <OneTimePasscode*
        • .value ends with '@odspnotify>'
  9. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  10. any of:
    • all of:
      • sender.email.domain.root_domain in $org_domains
      • any of:
        • not:
          • headers.auth_summary.dmarc.pass
        • not:
          • all of:
            • headers.auth_summary.dmarc.pass is missing
            • all of headers.domains where:
              • .root_domain in ('outlook.com', 'office365.com')
            • headers.message_id ends with 'protection.outlook.com>'
    • sender.email.domain.root_domain not in $org_domains
  11. not:
    • all of:
      • headers.message_id ends with 'mimecast.lan>'
      • all of filter(body.links) where all hold:
        • .href_url.domain.root_domain in ('mimecastprotect.com', 'mimecast.com')
        • any of .href_url.query_params_decoded['domain'] where any holds:
          • strings.parse_domain(.).tld is 'ms'
          • strings.parse_domain(.).root_domain is 'sharepoint.com'
  12. 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
  13. not:
    • profile.by_sender().any_messages_benign

Inspects: attachments[].file_type, body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.domain.root_domain, headers.auth_summary.dmarc.details.from.domain, headers.auth_summary.dmarc.details.from.root_domain, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.domains, headers.domains[].root_domain, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.hops[].fields[].value, headers.message_id, headers.return_path.email, sender.email.domain.domain, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.logo_detect, ml.nlu_classifier, profile.by_sender, strings.ends_with, strings.icontains, strings.iends_with, strings.ilike, strings.parse_domain, strings.starts_with. Reference lists: $file_types_images, $high_trust_sender_root_domains, $org_domains.

Indicators matched (34)

FieldMatchValue
attachments[].file_typeequalspdf
strings.starts_withprefixMicrosoft
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
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].nameequalscred_theft
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].confidencemembermedium
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].confidencememberhigh
headers.auth_summary.dmarc.details.from.domainmemberazureadnotifications.us
headers.auth_summary.dmarc.details.from.domainmembermicrosoft.com
headers.auth_summary.dmarc.details.from.domainmembersharepointonline.com
headers.auth_summary.dmarc.details.from.domainmembercloudappsecurity.com
22 more
headers.auth_summary.dmarc.details.from.domainmembermicrosoftsupport.com
headers.auth_summary.dmarc.details.from.domainmembermicrosoft.onmicrosoft.com
headers.auth_summary.dmarc.details.from.domainmemberyammer.com
sender.email.domain.domainequalsplanner.office365.com
headers.return_path.emailequalsnoreply@planner.office365.com
headers.auth_summary.dmarc.details.from.root_domainequalsoffice365.com
sender.email.domain.domainequalsmicrosoft.onmicrosoft.com
body.links[].href_url.domain.root_domainequalsmicrosoft.com
strings.ilikesubstring<Share-*
strings.ilikesubstring<MassDelete-*
strings.ilikesubstring<FileDeleteAfterExpiration-*
strings.ilikesubstring<NotifyOwnerSharedWithExternalUsers*
strings.ilikesubstring<OneTimePasscode*
strings.ends_withsuffix@odspnotify>
headers.hops[].fields[].nameequalsX-Google-Original-Message-ID
headers.domains[].root_domainmemberoutlook.com
headers.domains[].root_domainmemberoffice365.com
strings.ends_withsuffixprotection.outlook.com>
strings.iends_withsuffixmimecast.lan>
body.links[].display_textequalsOpen
filter(body.links)[].href_url.domain.root_domainmembermimecastprotect.com
filter(body.links)[].href_url.domain.root_domainmembermimecast.com