Detection rules › Sublime MQL

Brand impersonation: Sharepoint

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

Body, attached images or pdf contains a Sharepoint logo. The message contains a link and credential theft language.

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(body.links) > 0
and (
  any(attachments,
      (.file_type in $file_types_images or .file_type == "pdf")
      and any(ml.logo_detect(.).brands, .name == "Microsoft SharePoint")
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name == "Microsoft SharePoint"
  )
  or strings.istarts_with(strings.replace_confusables(body.current_thread.text),
                          "Sharepoint"
  )
  or regex.icontains(body.html.raw,
                     '<img.*(title=|alt=).share.*src=""'
  ) // broken Sharepoint logo
  or (
    strings.icontains(strings.replace_confusables(body.plain.raw), "SharePoint")
    // message body references file deletion
    and regex.icontains(body.plain.raw,
                        '(expired )?file\s(was|has been|will be|scheduled (for|to be))\s?delet(ed|ion)'
    )
  )
  or (
    strings.icontains(body.current_thread.text, 'Sharepoint')
    and (
      regex.icontains(body.html.raw, '{(?:domain|randomNumber\d?)}')
      or any(body.links,
             regex.icontains(.href_url.url, 'mailto:[^@]+@{domain}')
      )
      or regex.icontains(body.html.raw, '<title>[^<]*Easearch[^<]*</title>')
    )
  )
)
and (
  (
    any(ml.nlu_classifier(body.current_thread.text).intents,
        .name == "cred_theft" and .confidence == "high"
    )
    //
    // 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
    //
    or any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents,
           .name == "cred_theft" and .confidence == "high"
    )
  )
  or any(ml.nlu_classifier(body.current_thread.text).entities,
         .name == "urgency" and strings.ilike(.text, "*encrypted*")
  )
  or any(body.links,
         regex.imatch(.display_text,
                      '(?:re)?view (?:(?:&|and) (?:e([[:punct:]]|\s)?)?sign )?(?:complete )?(?:document|file)'
         )
  )
)
and not (
  (
    (
      strings.istarts_with(subject.subject, "RE:")
      or strings.istarts_with(subject.subject, "R:")
      or strings.istarts_with(subject.subject, "ODG:")
      or strings.istarts_with(subject.subject, "答复:")
      or strings.istarts_with(subject.subject, "AW:")
      or strings.istarts_with(subject.subject, "TR:")
      or strings.istarts_with(subject.subject, "FWD:")
      or regex.imatch(subject.subject, '(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:')
      or regex.imatch(subject.subject,
                      '^\[?(EXT|EXTERNAL)\]?[: ]\s*(RE|FWD?|FW|AW|TR|ODG|答复):.*'
      )
    )
    and (
      (length(headers.references) > 0 or headers.in_reply_to is not null)
      // ensure that there are actual threads
      and (
        length(body.previous_threads) > 0
        or (length(body.html.display_text) - length(body.current_thread.text)) > 200
      )
    )
  )
)
and (
  profile.by_sender_email().prevalence != 'common'
  or not profile.by_sender_email().solicited
  or profile.by_sender().any_messages_malicious_or_spam
)
and not profile.by_sender().any_messages_benign

// 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
)

// negate sharepoint file share
and not (
  // based on the message id format
  (
    (
      strings.starts_with(headers.message_id, '<Share-')
      and strings.ends_with(headers.message_id, '@odspnotify>')
    )
    // negate legitimate access request to file
    or (
      strings.starts_with(headers.message_id, '<Sharing')
      and strings.ends_with(headers.message_id, '@odspnotify>')
    )
    // deal with Google thinking the message ID is "broke"
    or (
      strings.icontains(headers.message_id, 'SMTPIN_ADDED_BROKEN')
      and any(headers.hops,
              any(.fields,
                  .name == "X-Google-Original-Message-ID"
                  and strings.starts_with(.value, '<Share-')
                  and strings.ends_with(.value, '@odspnotify>')
              )
      )
    )
  )
  // all of the "action" links are sharepoint/ms
  and all(filter(body.links,
                 strings.icontains(subject.subject, .display_text)
                 or .display_text == "Open"
          ),
          .href_url.domain.root_domain in ("sharepoint.com")
          or (
            .href_url.domain.tld == "ms"
            // Microsoft does not own the .ms TLD, this checks to ensure it is one of their domains
            and (
              network.whois(.href_url.domain).registrant_company == "Microsoft Corporation"
              or strings.ilike(network.whois(.href_url.domain).registrar_name,
                               "*MarkMonitor*",
                               "*CSC Corporate*",
                               "*com laude*"
              )
            )
          )
  )
)
// 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"
          )
  )
)

Detection logic

Scope: inbound message.

Body, attached images or pdf contains a Sharepoint logo. The message contains a link and credential theft language.

  1. inbound message
  2. length(body.links) > 0
  3. any of:
    • any of attachments where all hold:
      • any of:
        • .file_type in $file_types_images
        • .file_type is 'pdf'
      • any of ml.logo_detect(.).brands where:
        • .name is 'Microsoft SharePoint'
    • any of ml.logo_detect(file.message_screenshot()).brands where:
      • .name is 'Microsoft SharePoint'
    • strings.replace_confusables(body.current_thread.text) starts with 'Sharepoint'
    • body.html.raw matches '<img.*(title=|alt=).share.*src=""'
    • all of:
      • strings.replace_confusables(body.plain.raw) contains 'SharePoint'
      • body.plain.raw matches '(expired )?file\\s(was|has been|will be|scheduled (for|to be))\\s?delet(ed|ion)'
    • all of:
      • body.current_thread.text contains 'Sharepoint'
      • any of:
        • body.html.raw matches '{(?:domain|randomNumber\\d?)}'
        • any of body.links where:
          • .href_url.url matches 'mailto:[^@]+@{domain}'
        • body.html.raw matches '<title>[^<]*Easearch[^<]*</title>'
  4. any of:
    • any of:
      • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
        • .name is 'cred_theft'
        • .confidence is 'high'
      • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents where all hold:
        • .name is 'cred_theft'
        • .confidence is 'high'
    • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
      • .name is 'urgency'
      • .text matches '*encrypted*'
    • any of body.links where:
      • .display_text matches '(?:re)?view (?:(?:&|and) (?:e([[:punct:]]|\\s)?)?sign )?(?:complete )?(?:document|file)'
  5. not:
    • all of:
      • any of:
        • subject.subject starts with 'RE:'
        • subject.subject starts with 'R:'
        • subject.subject starts with 'ODG:'
        • subject.subject starts with '答复:'
        • subject.subject starts with 'AW:'
        • subject.subject starts with 'TR:'
        • subject.subject starts with 'FWD:'
        • subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?)\\s?:'
        • subject.subject matches '^\\[?(EXT|EXTERNAL)\\]?[: ]\\s*(RE|FWD?|FW|AW|TR|ODG|答复):.*'
      • all of:
        • any of:
          • length(headers.references) > 0
          • headers.in_reply_to is set
        • any of:
          • length(body.previous_threads) > 0
          • length(body.html.display_text) - length(body.current_thread.text) > 200
  6. any of:
    • profile.by_sender_email().prevalence is not 'common'
    • not:
      • profile.by_sender_email().solicited
    • profile.by_sender().any_messages_malicious_or_spam
  7. not:
    • profile.by_sender().any_messages_benign
  8. 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
  9. not:
    • all of:
      • any of:
        • all of:
          • headers.message_id starts with '<Share-'
          • headers.message_id ends with '@odspnotify>'
        • all of:
          • headers.message_id starts with '<Sharing'
          • headers.message_id ends with '@odspnotify>'
        • all of:
          • headers.message_id contains 'SMTPIN_ADDED_BROKEN'
          • any of headers.hops where:
            • any of .fields where all hold:
              • .name is 'X-Google-Original-Message-ID'
              • .value starts with '<Share-'
              • .value ends with '@odspnotify>'
      • all of filter(body.links) where any holds:
        • .href_url.domain.root_domain in ('sharepoint.com')
        • all of:
          • .href_url.domain.tld is 'ms'
          • any of:
            • network.whois(.href_url.domain).registrant_company is 'Microsoft Corporation'
            • network.whois(.href_url.domain).registrar_name matches any of 3 patterns
              • *MarkMonitor*
              • *CSC Corporate*
              • *com laude*
  10. 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'

Inspects: attachments[].file_type, body.current_thread.text, body.html.raw, body.links, body.links[].display_text, body.links[].href_url.url, body.plain.raw, body.previous_threads, headers.auth_summary.dmarc.pass, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.hops[].fields[].value, headers.in_reply_to, headers.message_id, headers.references, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.logo_detect, ml.nlu_classifier, network.whois, profile.by_sender, profile.by_sender_email, regex.icontains, regex.imatch, strings.ends_with, strings.icontains, strings.iends_with, strings.ilike, strings.istarts_with, strings.parse_domain, strings.replace_confusables, strings.starts_with. Reference lists: $file_types_images, $high_trust_sender_root_domains.

Indicators matched (41)

FieldMatchValue
attachments[].file_typeequalspdf
ml.logo_detect(attachments[]).brands[].nameequalsMicrosoft SharePoint
ml.logo_detect(file.message_screenshot()).brands[].nameequalsMicrosoft SharePoint
strings.istarts_withprefixSharepoint
regex.icontainsregex<img.*(title=|alt=).share.*src=""
strings.icontainssubstringSharePoint
regex.icontainsregex(expired )?file\s(was|has been|will be|scheduled (for|to be))\s?delet(ed|ion)
strings.icontainssubstringSharepoint
regex.icontainsregex{(?:domain|randomNumber\d?)}
regex.icontainsregexmailto:[^@]+@{domain}
regex.icontainsregex<title>[^<]*Easearch[^<]*</title>
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscred_theft
29 more
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].nameequalscred_theft
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].confidenceequalshigh
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsurgency
strings.ilikesubstring*encrypted*
regex.imatchregex(?:re)?view (?:(?:&|and) (?:e([[:punct:]]|\s)?)?sign )?(?:complete )?(?:document|file)
strings.istarts_withprefixRE:
strings.istarts_withprefixR:
strings.istarts_withprefixODG:
strings.istarts_withprefix答复:
strings.istarts_withprefixAW:
strings.istarts_withprefixTR:
strings.istarts_withprefixFWD:
regex.imatchregex(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:
regex.imatchregex^\[?(EXT|EXTERNAL)\]?[: ]\s*(RE|FWD?|FW|AW|TR|ODG|答复):.*
strings.starts_withprefix<Share-
strings.ends_withsuffix@odspnotify>
strings.starts_withprefix<Sharing
strings.icontainssubstringSMTPIN_ADDED_BROKEN
headers.hops[].fields[].nameequalsX-Google-Original-Message-ID
body.links[].display_textequalsOpen
filter(body.links)[].href_url.domain.root_domainmembersharepoint.com
filter(body.links)[].href_url.domain.tldequalsms
strings.ilikesubstring*MarkMonitor*
strings.ilikesubstring*CSC Corporate*
strings.ilikesubstring*com laude*
strings.iends_withsuffixmimecast.lan>
filter(body.links)[].href_url.domain.root_domainmembermimecastprotect.com
filter(body.links)[].href_url.domain.root_domainmembermimecast.com