Detection rules › Sublime MQL

Credential phishing: 'Secure message' and engaging language

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

Body contains language resembling credential theft, and a "secure message" from an untrusted sender.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesSocial engineering

Event coverage

Rule body MQL

type.inbound
and (
  (
    any(ml.nlu_classifier(body.current_thread.text).intents,
        .name == "cred_theft" and .confidence == "high"
    )
    // add fallback when NLU doesn't pick up cred theft to check for suspicious links
    or any(body.current_thread.links,
           regex.icontains(.display_text, '(?:read|view|open) the message')
           and (
             .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 any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents,
         .name == "cred_theft" and .confidence in ("medium", "high")
  )
)

// ----- other suspicious signals here -----
and (
  (
    regex.icontains(body.current_thread.text,
                    "secured? (message|directory|document)"
    )
    or strings.icontains(body.current_thread.text, "document portal")
    or strings.icontains(body.current_thread.text, "encrypted message")
    or strings.icontains(body.current_thread.text, "protected message")
  )
  or any(body.previous_threads,
         regex.icontains(.text, "secured? (message|directory|document)")
         or strings.icontains(.text, "document portal")
         or strings.icontains(.text, "encrypted message")
         or strings.icontains(.text, "protected message")
  )
  or any(body.current_thread.links,
         regex.icontains(ml.link_analysis(.).final_dom.display_text,
                         'secured? (?:message|directory|document) access'
         )
         or regex.icontains(ml.link_analysis(.).final_dom.inner_text,
                            'secured? (?:message|directory|document) access'
         )
  )
  or (
    length(filter(ml.nlu_classifier(body.current_thread.text).entities,
                  .name == "urgency"
           )
    ) >= 2
    and any(ml.nlu_classifier(body.current_thread.text).topics,
            .name == "Secure Message" and .confidence != "low"
    )
  )
)
// todo: automated display name / human local part
// todo: suspicious link (unfurl click trackers)

// ----------

// has at least 1 link
and length(body.links) > 0

// negate legitimate message senders
and (
  sender.email.domain.root_domain not in ("protectedtrust.com")
  and any(body.links,
          .href_url.domain.root_domain != sender.email.domain.root_domain
  )
  // negate known secure mailers
  and not all(body.links,
              .href_url.domain.root_domain in (
                "mimecast.com",
                "cisco.com",
                "csiesafe.com"
              )
  )
  and any(headers.hops,
          .index == 0
          and not any(.fields,
                      strings.contains(.value,
                                       'multipart/mixed; boundary="PROOFPOINT_BOUNDARY_1"'
                      )
          )
  )
  and not (
    length(filter(attachments,
                  strings.ilike(.file_name,
                                "logo.*",
                                "lock.gif",
                                "SecureMessageAtt.html"
                  )
           )
    ) == 3
    and any(attachments,
            .file_type == "html"
            and any(file.explode(.),
                    .scan.html.title == "Proofpoint Encryption"
                    and any(.scan.url.urls,
                            strings.iends_with(.path,
                                               'formpostdir/safeformpost.aspx'
                            )
                    )
            )
            and strings.count(file.parse_html(.).raw, 'name="msg') > 3
    )
  )
  and not (
    any(headers.hops,
        any(.fields,
            .name in (
              'X-ZixNet',
              'X-VPM-MIV',
              'X-VPM-ActionCode',
              'X-VPM-SmtpTo'
            )
        )
    )
    and any(headers.domains,
            .root_domain in (
              "zixport.com",
              "zixcorp.com",
              "zixmail.net",
              "zixworks.com"
            )
    )
  )
  and not (
    any(headers.hops, any(.fields, .name == 'X-SendInc-Message-Id'))
    and any(headers.domains, .root_domain in ("sendinc.net"))
  )
  // negating Mimecast sends with MS banner and/or sender's email pulled out as a link
  and not length(filter(body.links,
                        (
                          .display_text is null
                          and .display_url.url == sender.email.domain.root_domain
                        )
                        or .href_url.domain.root_domain in (
                          "aka.ms",
                          "mimecast.com",
                          "cisco.com"
                        )
                 )
  ) == length(body.links)
)
and (
  (
    profile.by_sender().prevalence in ("new", "outlier")
    and not profile.by_sender().solicited
  )
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
  // or the sender is all undisclosed or there are no recipients
  or (
    length(recipients.to) == 0
    or all(recipients.to,
           strings.ilike(.display_name, "undisclosed?recipients")
    )
  )
  // or the sender exhibits a "self sender" pattern
  or (
    length(recipients.to) == 1
    and any(recipients.to, .email.email == sender.email.email)
  )
)
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
)

Detection logic

Scope: inbound message.

Body contains language resembling credential theft, and a "secure message" from an untrusted sender.

  1. inbound message
  2. 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 body.current_thread.links where all hold:
        • .display_text matches '(?:read|view|open) the message'
        • 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
    • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents where all hold:
      • .name is 'cred_theft'
      • .confidence in ('medium', 'high')
  3. any of:
    • any of:
      • body.current_thread.text matches 'secured? (message|directory|document)'
      • body.current_thread.text contains 'document portal'
      • body.current_thread.text contains 'encrypted message'
      • body.current_thread.text contains 'protected message'
    • any of body.previous_threads where any holds:
      • .text matches 'secured? (message|directory|document)'
      • .text contains 'document portal'
      • .text contains 'encrypted message'
      • .text contains 'protected message'
    • any of body.current_thread.links where any holds:
      • ml.link_analysis(.).final_dom.display_text matches 'secured? (?:message|directory|document) access'
      • ml.link_analysis(.).final_dom.inner_text matches 'secured? (?:message|directory|document) access'
    • all of:
      • length(filter(ml.nlu_classifier(body.current_thread.text).entities, .name == 'urgency')) ≥ 2
      • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
        • .name is 'Secure Message'
        • .confidence is not 'low'
  4. length(body.links) > 0
  5. all of:
    • sender.email.domain.root_domain not in ('protectedtrust.com')
    • any of body.links where:
      • .href_url.domain.root_domain is not sender.email.domain.root_domain
    • not:
      • all of body.links where:
        • .href_url.domain.root_domain in ('mimecast.com', 'cisco.com', 'csiesafe.com')
    • any of headers.hops where all hold:
      • .index is 0
      • not:
        • any of .fields where:
          • .value contains 'multipart/mixed; boundary="PROOFPOINT_BOUNDARY_1"'
    • not:
      • all of:
        • length(filter(attachments, strings.ilike(.file_name, 'logo.*', 'lock.gif', 'SecureMessageAtt.html'))) is 3
        • any of attachments where all hold:
          • .file_type is 'html'
          • any of file.explode(.) where all hold:
            • .scan.html.title is 'Proofpoint Encryption'
            • any of .scan.url.urls where:
              • .path ends with 'formpostdir/safeformpost.aspx'
          • strings.count(file.parse_html(.).raw, 'name="msg') > 3
    • not:
      • all of:
        • any of headers.hops where:
          • any of .fields where:
            • .name in ('X-ZixNet', 'X-VPM-MIV', 'X-VPM-ActionCode', 'X-VPM-SmtpTo')
        • any of headers.domains where:
          • .root_domain in ('zixport.com', 'zixcorp.com', 'zixmail.net', 'zixworks.com')
    • not:
      • all of:
        • any of headers.hops where:
          • any of .fields where:
            • .name is 'X-SendInc-Message-Id'
        • any of headers.domains where:
          • .root_domain in ('sendinc.net')
    • not:
      • length(filter(body.links, .display_text is null and .display_url.url == sender.email.domain.root_domain or .href_url.domain.root_domain in ('aka.ms', 'mimecast.com', 'cisco.com'))) is length(body.links)
  6. any of:
    • all of:
      • profile.by_sender().prevalence in ('new', 'outlier')
      • not:
        • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
    • any of:
      • length(recipients.to) is 0
      • all of recipients.to where:
        • .display_name matches 'undisclosed?recipients'
    • all of:
      • length(recipients.to) is 1
      • any of recipients.to where:
        • .email.email is sender.email.email
  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

Inspects: attachments[].file_name, attachments[].file_type, body.current_thread.links, body.current_thread.links[].display_text, body.current_thread.links[].href_url.domain.domain, body.current_thread.links[].href_url.domain.root_domain, body.current_thread.text, body.links, body.links[].display_text, body.links[].display_url.url, body.links[].href_url.domain.root_domain, body.previous_threads, body.previous_threads[].text, headers.auth_summary.dmarc.pass, headers.domains, headers.domains[].root_domain, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.hops[].fields[].value, headers.hops[].index, recipients.to, recipients.to[].display_name, recipients.to[].email.email, sender.email.domain.root_domain, sender.email.email, type.inbound. Sensors: beta.ocr, file.explode, file.message_screenshot, file.parse_html, ml.link_analysis, ml.nlu_classifier, profile.by_sender, regex.icontains, strings.contains, strings.count, strings.icontains, strings.iends_with, strings.ilike. Reference lists: $free_file_hosts, $free_subdomain_hosts, $high_trust_sender_root_domains, $social_landing_hosts, $tranco_1m, $url_shorteners.

Indicators matched (36)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscred_theft
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh
regex.icontainsregex(?:read|view|open) the message
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
regex.icontainsregexsecured? (message|directory|document)
strings.icontainssubstringdocument portal
strings.icontainssubstringencrypted message
strings.icontainssubstringprotected message
regex.icontainsregexsecured? (?:message|directory|document) access
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsurgency
24 more
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsSecure Message
sender.email.domain.root_domainmemberprotectedtrust.com
body.links[].href_url.domain.root_domainmembermimecast.com
body.links[].href_url.domain.root_domainmembercisco.com
body.links[].href_url.domain.root_domainmembercsiesafe.com
strings.containssubstringmultipart/mixed; boundary="PROOFPOINT_BOUNDARY_1"
strings.ilikesubstringlogo.*
strings.ilikesubstringlock.gif
strings.ilikesubstringSecureMessageAtt.html
attachments[].file_typeequalshtml
file.explode(attachments[])[].scan.html.titleequalsProofpoint Encryption
strings.iends_withsuffixformpostdir/safeformpost.aspx
headers.hops[].fields[].namememberX-ZixNet
headers.hops[].fields[].namememberX-VPM-MIV
headers.hops[].fields[].namememberX-VPM-ActionCode
headers.hops[].fields[].namememberX-VPM-SmtpTo
headers.domains[].root_domainmemberzixport.com
headers.domains[].root_domainmemberzixcorp.com
headers.domains[].root_domainmemberzixmail.net
headers.domains[].root_domainmemberzixworks.com
headers.hops[].fields[].nameequalsX-SendInc-Message-Id
headers.domains[].root_domainmembersendinc.net
body.links[].href_url.domain.root_domainmemberaka.ms
strings.ilikesubstringundisclosed?recipients