Detection rules › Sublime MQL

Sublime MQL rules: callback

Brand impersonation: GitHub with callback scam indicators

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

Detects messages using GitHub's noreply address that contain callback scam language, brand impersonation tactics, or fraudulent purchase/payment content with phone numbers for victim contact.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesImpersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and sender.email.email == 'noreply@github.com'
and length(attachments) == 0
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name == "callback_scam" and .confidence != "low"
  )
  or (
    regex.icontains(body.current_thread.text,
                    (
                      "mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck"
                    )
    )
    and (
      3 of (
        strings.ilike(body.current_thread.text, '*purchase*'),
        strings.ilike(body.current_thread.text, '*payment*'),
        strings.ilike(body.current_thread.text, '*transaction*'),
        strings.ilike(body.current_thread.text, '*subscription*'),
        strings.ilike(body.current_thread.text, '*antivirus*'),
        strings.ilike(body.current_thread.text, '*order*'),
        strings.ilike(body.current_thread.text, '*support*'),
        strings.ilike(body.current_thread.text, '*receipt*'),
        strings.ilike(body.current_thread.text, '*invoice*'),
        strings.ilike(body.current_thread.text, '*call*'),
        strings.ilike(body.current_thread.text, '*cancel*'),
        strings.ilike(body.current_thread.text, '*renew*'),
        strings.ilike(body.current_thread.text, '*refund*'),
        strings.ilike(body.current_thread.text, '*host key*')
      )
    )
    // phone number regex
    and any([body.current_thread.text, subject.subject],
            regex.icontains(.,
                            '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                            '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
            )
    )
  )
)

Detection Logic

Scope: inbound message.

Detects messages using GitHub's noreply address that contain callback scam language, brand impersonation tactics, or fraudulent purchase/payment content with phone numbers for victim contact.

  1. inbound message
  2. sender.email.email is 'noreply@github.com'
  3. length(attachments) == 0
  4. any of:
    • any(ml.nlu_classifier(body.current_thread.text).intents, .name == "callback_scam" and .confidence != "low" )
    • all of:
      • body.current_thread.text matches 'mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck'
      • at least 3 of 14: body.current_thread.text matches any of 14 patterns
        • *purchase*
        • *payment*
        • *transaction*
        • *subscription*
        • *antivirus*
        • *order*
        • *support*
        • *receipt*
        • *invoice*
        • *call*
        • *cancel*
        • *renew*
        • *refund*
        • *host key*
      • any([body.current_thread.text, subject.subject], regex.icontains(., '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' ) )

Inspects: body.current_thread.text, sender.email.email, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.ilike.

Indicators Matched (19)

FieldMatchValue
sender.email.emailequalsnoreply@github.com
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscallback_scam
regex.icontainsregexmcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*receipt*
strings.ilikesubstring*invoice*
7 more
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
strings.ilikesubstring*host key*
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      body.current_thread.text regex_match "mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck"
       macro "3 of (\n        strings.ilike(body.current_thread.text, '*purchase*'),\n        strings.ilike(body.current_thread.text, '*payment*'),\n        strings.ilike(body.current_thread.text, '*transaction*'),\n        strings.ilike(body.current_thread.text, '*subscription*'),\n        strings.ilike(body.current_thread.text, '*antivirus*'),\n        strings.ilike(body.current_thread.text, '*order*'),\n        strings.ilike(body.current_thread.text, '*support*'),\n        strings.ilike(body.current_thread.text, '*receipt*'),\n        strings.ilike(body.current_thread.text, '*invoice*'),\n        strings.ilike(body.current_thread.text, '*call*'),\n        strings.ilike(body.current_thread.text, '*cancel*'),\n        strings.ilike(body.current_thread.text, '*renew*'),\n        strings.ilike(body.current_thread.text, '*refund*'),\n        strings.ilike(body.current_thread.text, '*host key*')\n      )"
       macro "any([body.current_thread.text, subject.subject],\n            regex.icontains(.,\n                            '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                            '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n            )\n    )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n      .name == \"callback_scam\" and .confidence != \"low\"\n  )"
  attachments length_compare "0"
  sender.email.email eq "noreply@github.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
    • mcafee
    • n[o0]rt[o0]n
    • geek.{0,5}squad
    • paypal
    • ebay
    • symantec
    • best buy
    • lifel[o0]ck
    corpus 7 (mql 7)
field:"body.current_thread.text" kind:regex_match
sender.email.emaileq
  • noreply@github.com
field:"sender.email.email" kind:eq value:"noreply@github.com"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing in body or attachment (untrusted sender)

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

Detects callback scams by analyzing text within images of receipts or invoices from untrusted senders.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesOut of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • body.html
  • headers
  • headers.auth_summary
  • headers.domains
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) < 5
and (
  any(attachments,
      (
        .file_type in $file_types_images
        or .file_type in ("pdf", "xlsx", "docx")
      )
      and (
        any(ml.nlu_classifier(beta.ocr(.).text).intents,
            .name == "callback_scam" and .confidence in ("medium", "high")
        )
        or any(file.explode(.),

               // exclude images taken with mobile cameras and screenshots from android
               not any(.scan.exiftool.fields,
                       .key == "Model"
                       or (
                         .key == "Software"
                         and strings.starts_with(.value, "Android")
                       )
                       or (.key == "UserComment" and .value == "Screenshot")
               )
               and any(ml.nlu_classifier(.scan.ocr.raw).intents,
                       .name == "callback_scam"
                       and .confidence in ("medium", "high")
               )
        )
      )
      and (
        // negate noreply unless a logo is found in the attachment
        (
          sender.email.local_part in ("no_reply", "noreply")
          and any(ml.logo_detect(.).brands,
                  .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee")
          )
        )
        or sender.email.local_part not in ("no_reply", "noreply")
      )
  )
  or (
    any(ml.nlu_classifier(body.current_thread.text).intents,
        .name in ("callback_scam") and .confidence in ("medium", "high")
    )
    and (
      (
        270 < length(body.current_thread.text) < 1750
        or (
          75 < length(body.current_thread.text) < 1750
          and (
            strings.ilike(body.current_thread.text,
                          "*PayPal*",
                          "*Norton*",
                          "*GeekSquad*",
                          "*Ebay*",
                          "*McAfee*",
                          "*=1"
            )
            // phone number regex
            or regex.icontains(body.current_thread.text,
                               '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                               '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
            )
            or 1 of (
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "geek squad"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "lifelock"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "best buy"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "mcafee"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "norton"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "ebay"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "paypal"
              ),
              strings.icontains(beta.ocr(file.message_screenshot()).text,
                                "virus"
              ),
            )
          )
        )
      )
    )
  )
)
and not (
  any(headers.domains, .domain == "smtp-out.gcp.bigcommerce.net")
  and strings.icontains(body.html.raw, "bigcommerce.com")
)
and (
  not profile.by_sender_email().solicited
  or (
    profile.by_sender_email().any_messages_malicious_or_spam
    and not profile.by_sender_email().any_messages_benign
  )
)

// negate highly trusted sender domains unless they fail DMARC authentication
and not (
  sender.email.domain.root_domain in $high_trust_sender_root_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)
// negate opsgenie email notifications
and not sender.email.email == 'opsgenie@opsgenie.net'

Detection Logic

Scope: inbound message.

Detects callback scams by analyzing text within images of receipts or invoices from untrusted senders.

  1. inbound message
  2. length(attachments) < 5
  3. any of:
    • any of attachments where all hold:
      • any of:
        • .file_type in $file_types_images
        • .file_type in ('pdf', 'xlsx', 'docx')
      • any of:
        • any(ml.nlu_classifier(beta.ocr(.).text).intents, .name == "callback_scam" and .confidence in ("medium", "high") )
        • any(file.explode(.), // exclude images taken with mobile cameras and screenshots from android not any(.scan.exiftool.fields, .key == "Model" or ( .key == "Software" and strings.starts_with(.value, "Android") ) or (.key == "UserComment" and .value == "Screenshot") ) and any(ml.nlu_classifier(.scan.ocr.raw).intents, .name == "callback_scam" and .confidence in ("medium", "high") ) )
      • any of:
        • all of:
          • sender.email.local_part in ('no_reply', 'noreply')
          • any(ml.logo_detect(.).brands, .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee") )
        • sender.email.local_part not in ('no_reply', 'noreply')
    • all of:
      • any(ml.nlu_classifier(body.current_thread.text).intents, .name in ("callback_scam") and .confidence in ("medium", "high") )
      • any of:
        • all of:
          • (270) < (length(body.current_thread.text))
          • (length(body.current_thread.text)) < (1750)
        • all of:
          • all of:
            • (75) < (length(body.current_thread.text))
            • (length(body.current_thread.text)) < (1750)
          • any of:
            • body.current_thread.text matches any of 6 patterns
              • *PayPal*
              • *Norton*
              • *GeekSquad*
              • *Ebay*
              • *McAfee*
              • *=1
            • body.current_thread.text matches any of 2 patterns
              • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
              • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
            • at least 1 of:
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "geek squad" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "lifelock" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "best buy" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "mcafee" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "norton" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "ebay" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "paypal" )
              • strings.icontains(beta.ocr(file.message_screenshot()).text, "virus" )
  4. not:
    • all of:
      • any of headers.domains where:
        • .domain is 'smtp-out.gcp.bigcommerce.net'
      • body.html.raw contains 'bigcommerce.com'
  5. any of:
    • not:
      • profile.by_sender_email().solicited
    • all of:
      • profile.by_sender_email().any_messages_malicious_or_spam
      • not:
        • profile.by_sender_email().any_messages_benign
  6. not:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • coalesce(headers.auth_summary.dmarc.pass, false)
  7. not:
    • sender.email.email is 'opsgenie@opsgenie.net'

Inspects: attachments[].file_type, body.current_thread.text, body.html.raw, headers.auth_summary.dmarc.pass, headers.domains, headers.domains[].domain, sender.email.domain.root_domain, sender.email.email, sender.email.local_part, type.inbound. Sensors: beta.ocr, file.explode, file.message_screenshot, ml.logo_detect, ml.nlu_classifier, profile.by_sender_email, regex.icontains, strings.icontains, strings.ilike, strings.starts_with. Reference lists: $file_types_images, $high_trust_sender_root_domains.

Indicators Matched (35)

FieldMatchValue
attachments[].file_typememberpdf
attachments[].file_typememberxlsx
attachments[].file_typememberdocx
ml.nlu_classifier(beta.ocr(attachments[]).text).intents[].nameequalscallback_scam
ml.nlu_classifier(beta.ocr(attachments[]).text).intents[].confidencemembermedium
ml.nlu_classifier(beta.ocr(attachments[]).text).intents[].confidencememberhigh
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].nameequalscallback_scam
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].confidencemembermedium
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].confidencememberhigh
sender.email.local_partmemberno_reply
sender.email.local_partmembernoreply
ml.logo_detect(attachments[]).brands[].namememberPayPal
23 more
ml.logo_detect(attachments[]).brands[].namememberNorton
ml.logo_detect(attachments[]).brands[].namememberGeekSquad
ml.logo_detect(attachments[]).brands[].namememberEbay
ml.logo_detect(attachments[]).brands[].namememberMcAfee
ml.nlu_classifier(body.current_thread.text).intents[].namemembercallback_scam
ml.nlu_classifier(body.current_thread.text).intents[].confidencemembermedium
ml.nlu_classifier(body.current_thread.text).intents[].confidencememberhigh
strings.ilikesubstring*PayPal*
strings.ilikesubstring*Norton*
strings.ilikesubstring*GeekSquad*
strings.ilikesubstring*Ebay*
strings.ilikesubstring*McAfee*
strings.ilikesubstring*=1
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringgeek squad
strings.icontainssubstringlifelock
strings.icontainssubstringbest buy
strings.icontainssubstringmcafee
strings.icontainssubstringnorton
strings.icontainssubstringebay
strings.icontainssubstringpaypal
strings.icontainssubstringvirus

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        and
          or
            body.current_thread.text ends_with "=1"
            body.current_thread.text match "Ebay"
            body.current_thread.text match "GeekSquad"
            body.current_thread.text match "McAfee"
            body.current_thread.text match "Norton"
            body.current_thread.text match "PayPal"
            body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
            body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"best buy\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"ebay\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"geek squad\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"lifelock\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"mcafee\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"norton\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"paypal\"\n              )"
            strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text,\n                                \"virus\"\n              )"
          body.current_thread.text length_compare "75"
          body.current_thread.text length_compare "1750"
        and
          body.current_thread.text length_compare "270"
          body.current_thread.text length_compare "1750"
       macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n        .name in (\"callback_scam\") and .confidence in (\"medium\", \"high\")\n    )"
    any(attachments)
      and
        or
          and
            sender.email.local_part in ["no_reply", "noreply"]
             macro "any(ml.logo_detect(.).brands,\n                  .name in (\"PayPal\", \"Norton\", \"GeekSquad\", \"Ebay\", \"McAfee\")\n          )"
          not
            sender.email.local_part in ["no_reply", "noreply"]
        or
          attachments.file_type in ["docx", "pdf", "xlsx"]
          attachments.file_type macro "attachments[].file_type in file_types_images"
        or
           macro "any(file.explode(.),\n\n               // exclude images taken with mobile cameras and screenshots from android\n               not any(.scan.exiftool.fields,\n                       .key == \"Model\"\n                       or (\n                         .key == \"Software\"\n                         and strings.starts_with(.value, \"Android\")\n                       )\n                       or (.key == \"UserComment\" and .value == \"Screenshot\")\n               )\n               and any(ml.nlu_classifier(.scan.ocr.raw).intents,\n                       .name == \"callback_scam\"\n                       and .confidence in (\"medium\", \"high\")\n               )\n        )"
           macro "any(ml.nlu_classifier(beta.ocr(.).text).intents,\n            .name == \"callback_scam\" and .confidence in (\"medium\", \"high\")\n        )"
  not
    and
      any(headers.domains)
        headers.domains.domain eq "smtp-out.gcp.bigcommerce.net"
      body.html.raw contains "bigcommerce.com"
  or
    and
      not
        profile.by_sender_email func_call "profile.by_sender_email().any_messages_benign"
      profile.by_sender_email func_call "profile.by_sender_email().any_messages_malicious_or_spam"
    not
      profile.by_sender_email func_call "profile.by_sender_email().solicited"
  not
    and
      headers.auth_summary.dmarc.pass eq "true"
      headers.auth_summary.dmarc.pass is_not_null
      sender.email.domain.root_domain macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
  not
    sender.email.email eq "opsgenie@opsgenie.net"
  attachments length_compare "5"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
body.current_thread.textwildcard
  • *=1
  • *Ebay* corpus 3 (mql 3)
  • *GeekSquad*
  • *McAfee* corpus 3 (mql 3)
  • *Norton* corpus 3 (mql 3)
  • *PayPal* corpus 4 (mql 4)
field:"body.current_thread.text" kind:wildcard
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
headers.domainsarray_any(no value, null check)excludes:headers.domains
body.html.rawcontainsbigcommerce.comexcludes:body.html.raw field:"body.html.raw" value:"bigcommerce.com"
headers.auth_summary.dmarc.passeqtrueexcludes:headers.auth_summary.dmarc.pass field:"headers.auth_summary.dmarc.pass" value:"true"
headers.auth_summary.dmarc.passis_not_null(no value, null check)excludes:headers.auth_summary.dmarc.pass
sender.email.emaileqopsgenie@opsgenie.netexcludes:sender.email.email field:"sender.email.email" value:"opsgenie@opsgenie.net"

Callback phishing via Adobe Sign comment

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

This rule inspects messages originating from legitimate Adobe Sign infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesEvasion, Impersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) == 0

// Legitimate Docusign sending infratructure
and sender.email.domain.root_domain == 'adobesign.com'
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
and (
  // this section is synced with attachment_callback_phish_with_pdf.yml and attachment_callback_phish_with_img.yml
  regex.icontains(strings.replace_confusables(body.current_thread.text),
                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T")
  )
)
and (
  (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // however, the 3 of logic and requiring a phone number is specific to this rule in order to reduce FPs
    // caused by messages which mention cancelling or otherwise managing a subscription
    // it is also synced and below for message_screenshot OCR output
    3 of (
      strings.icontains(body.current_thread.text, 'purchase'),
      strings.icontains(body.current_thread.text, 'payment'),
      strings.icontains(body.current_thread.text, 'transaction'),
      strings.icontains(body.current_thread.text, 'subscription'),
      strings.icontains(body.current_thread.text, 'antivirus'),
      strings.icontains(body.current_thread.text, 'order'),
      strings.icontains(body.current_thread.text, 'support'),
      strings.icontains(body.current_thread.text, 'help line'),
      strings.icontains(body.current_thread.text, 'receipt'),
      strings.icontains(body.current_thread.text, 'invoice'),
      strings.icontains(body.current_thread.text, 'call'),
      strings.icontains(body.current_thread.text, 'cancel'),
      strings.icontains(body.current_thread.text, 'renew'),
      strings.icontains(body.current_thread.text, 'refund'),
      regex.icontains(body.current_thread.text, "(?:reach|contact) us at"),
      strings.icontains(body.current_thread.text, "+1"),
      strings.icontains(body.current_thread.text, "amount"),
      strings.icontains(body.current_thread.text, "charged"),
      strings.icontains(body.current_thread.text, "crypto"),
      strings.icontains(body.current_thread.text, "wallet address"),
      regex.icontains(body.current_thread.text, '\$\d{3}\.\d{2}\b'),
    )
    // phone number regex
    and regex.icontains(body.current_thread.text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )
  )
  or (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // and above for current_thread.text
    //
    // 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
    //
    3 of (
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'subscription'
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      "(?:reach|contact) us at"
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'wallet address'
      ),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      '\$\d{3}\.\d{2}\b'
      ),
    )
    // phone number regex
    and regex.icontains(beta.ocr(file.message_screenshot()).text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )

    // negate messages with previous threads.  While callback phishing with thread hijacking or with current_thread 
    // padded with whitespace and previous threads in the message has been observed, the intetion of using OCR is for image embedded callbacks
    and not regex.icount(beta.ocr(file.message_screenshot()).text,
                         '(?:from|to|sent|date|cc|subject):'
    ) > 3
    // this notation of previous threads often only occurs once
    and not regex.icontains(beta.ocr(file.message_screenshot()).text,
                            'wrote:[\r\n]'
    )
  )
)

Detection Logic

Scope: inbound message.

This rule inspects messages originating from legitimate Adobe Sign infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

  1. inbound message
  2. length(attachments) == 0
  3. sender.email.domain.root_domain is 'adobesign.com'
  4. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  5. any of:
    • regex.icontains(strings.replace_confusables(body.current_thread.text), '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)' )
    • any(ml.logo_detect(file.message_screenshot()).brands, .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T") )
  6. any of:
    • all of:
      • at least 3 of:
        • body.current_thread.text contains 'purchase'
        • body.current_thread.text contains 'payment'
        • body.current_thread.text contains 'transaction'
        • body.current_thread.text contains 'subscription'
        • body.current_thread.text contains 'antivirus'
        • body.current_thread.text contains 'order'
        • body.current_thread.text contains 'support'
        • body.current_thread.text contains 'help line'
        • body.current_thread.text contains 'receipt'
        • body.current_thread.text contains 'invoice'
        • body.current_thread.text contains 'call'
        • body.current_thread.text contains 'cancel'
        • body.current_thread.text contains 'renew'
        • body.current_thread.text contains 'refund'
        • body.current_thread.text matches '(?:reach|contact) us at'
        • body.current_thread.text contains '+1'
        • body.current_thread.text contains 'amount'
        • body.current_thread.text contains 'charged'
        • body.current_thread.text contains 'crypto'
        • body.current_thread.text contains 'wallet address'
        • body.current_thread.text matches '\\$\\d{3}\\.\\d{2}\\b'
      • body.current_thread.text matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
    • all of:
      • at least 3 of:
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'subscription' )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'order')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'support')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'call')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund')
        • regex.icontains(beta.ocr(file.message_screenshot()).text, "(?:reach|contact) us at" )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, '+1')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'wallet address' )
        • regex.icontains(beta.ocr(file.message_screenshot()).text, '\$\d{3}\.\d{2}\b' )
      • regex.icontains(beta.ocr(file.message_screenshot()).text, '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' )
      • not:
        • regex.icount(beta.ocr(file.message_screenshot()).text, '(?:from|to|sent|date|cc|subject):' ) > 3
      • not:
        • regex.icontains(beta.ocr(file.message_screenshot()).text, 'wrote:[\r\n]' )

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.email.domain.root_domain, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.logo_detect, regex.icontains, regex.icount, strings.icontains, strings.replace_confusables.

Indicators Matched (32)

FieldMatchValue
sender.email.domain.root_domainequalsadobesign.com
regex.icontainsregex(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
ml.logo_detect(file.message_screenshot()).brands[].namememberMcAfee
ml.logo_detect(file.message_screenshot()).brands[].namememberAT&T
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
strings.icontainssubstringsubscription
20 more
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
regex.icontainsregex(?:reach|contact) us at
strings.icontainssubstring+1
strings.icontainssubstringamount
strings.icontainssubstringcharged
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringhelpdesk

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
        body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
       macro "3 of (\n      strings.icontains(body.current_thread.text, 'purchase'),\n      strings.icontains(body.current_thread.text, 'payment'),\n      strings.icontains(body.current_thread.text, 'transaction'),\n      strings.icontains(body.current_thread.text, 'subscription'),\n      strings.icontains(body.current_thread.text, 'antivirus'),\n      strings.icontains(body.current_thread.text, 'order'),\n      strings.icontains(body.current_thread.text, 'support'),\n      strings.icontains(body.current_thread.text, 'help line'),\n      strings.icontains(body.current_thread.text, 'receipt'),\n      strings.icontains(body.current_thread.text, 'invoice'),\n      strings.icontains(body.current_thread.text, 'call'),\n      strings.icontains(body.current_thread.text, 'cancel'),\n      strings.icontains(body.current_thread.text, 'renew'),\n      strings.icontains(body.current_thread.text, 'refund'),\n      regex.icontains(body.current_thread.text, \"(?:reach|contact) us at\"),\n      strings.icontains(body.current_thread.text, \"+1\"),\n      strings.icontains(body.current_thread.text, \"amount\"),\n      strings.icontains(body.current_thread.text, \"charged\"),\n      strings.icontains(body.current_thread.text, \"crypto\"),\n      strings.icontains(body.current_thread.text, \"wallet address\"),\n      regex.icontains(body.current_thread.text, '\\$\\d{3}\\.\\d{2}\\b'),\n    )"
    and
      not
        regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                            'wrote:[\\r\\n]'\n    )"
      not
        regex.icount func_call "regex.icount(beta.ocr(file.message_screenshot()).text,\n                         '(?:from|to|sent|date|cc|subject):'\n    ) > 3"
      regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n    )"
       macro "3 of (\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'subscription'\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      \"(?:reach|contact) us at\"\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'wallet address'\n      ),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      '\\$\\d{3}\\.\\d{2}\\b'\n      ),\n    )"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  or
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'\n  )"
     macro "any(ml.logo_detect(file.message_screenshot()).brands,\n         .name in (\"PayPal\", \"Norton\", \"GeekSquad\", \"Ebay\", \"McAfee\", \"AT&T\")\n  )"
  attachments length_compare "0"
  sender.email.domain.root_domain eq "adobesign.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
sender.email.domain.root_domaineq
  • adobesign.com
field:"sender.email.domain.root_domain" kind:eq value:"adobesign.com"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via calendar invite

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

Detects calendar invites containing callback phishing language in the DESCRIPTION or SUMMARY of the invite.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesSocial engineering, Evasion, ICS Phishing

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) > 0
and all(attachments, .content_type in ("text/calendar", "application/ics"))
and any(attachments,
        // extract the calendar invite summary or description and use NLU against it
        any(file.explode(.),
            any(.scan.ics.calendars,
                any(.components,
                    any(ml.nlu_classifier(.summary).intents,
                        .name == "callback_scam" and .confidence == "high"
                    )
                    or (
                      any(ml.nlu_classifier(.description).intents,
                          .name == "callback_scam"
                      )
                      or any(ml.nlu_classifier(strings.parse_html(.description).display_text
                             ).intents,
                             .name == "callback_scam"
                      )
                      or (
                        any(ml.nlu_classifier(.description).topics,
                            .name == "Request to View Invoice"
                            and .confidence == "high"
                        )
                        // emoji regex
                        and regex.contains(.description,
                                           '[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{FE00}-\x{FE0F}\x{200D}\x{20E3}\x{E0020}-\x{E007F}]'
                        )
                      )
                    )
                )
            )
        )
)
and (
  not profile.by_sender_email().solicited
  and not profile.by_sender_email().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 coalesce(headers.auth_summary.dmarc.pass, false)
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)

Detection Logic

Scope: inbound message.

Detects calendar invites containing callback phishing language in the DESCRIPTION or SUMMARY of the invite.

  1. inbound message
  2. length(attachments) > 0
  3. all of attachments where:
    • .content_type in ('text/calendar', 'application/ics')
  4. any of attachments where:
    • any(file.explode(.), any(.scan.ics.calendars, any(.components, any(ml.nlu_classifier(.summary).intents, .name == "callback_scam" and .confidence == "high" ) or ( any(ml.nlu_classifier(.description).intents, .name == "callback_scam" ) or any(ml.nlu_classifier(strings.parse_html(.description).display_text ).intents, .name == "callback_scam" ) or ( any(ml.nlu_classifier(.description).topics, .name == "Request to View Invoice" and .confidence == "high" ) // emoji regex and regex.contains(.description, '[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{FE00}-\x{FE0F}\x{200D}\x{20E3}\x{E0020}-\x{E007F}]' ) ) ) ) ) )
  5. all of:
    • not:
      • profile.by_sender_email().solicited
    • not:
      • profile.by_sender_email().any_messages_benign
  6. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • coalesce(headers.auth_summary.dmarc.pass, false)
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains

Inspects: attachments[].content_type, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, type.inbound. Sensors: file.explode, ml.nlu_classifier, profile.by_sender_email, regex.contains, strings.parse_html. Reference lists: $high_trust_sender_root_domains.

Indicators Matched (9)

FieldMatchValue
attachments[].content_typemembertext/calendar
attachments[].content_typememberapplication/ics
ml.nlu_classifier(file.explode(attachments[])[].scan.ics.calendars[].components[].summary).intents[].nameequalscallback_scam
ml.nlu_classifier(file.explode(attachments[])[].scan.ics.calendars[].components[].summary).intents[].confidenceequalshigh
ml.nlu_classifier(file.explode(attachments[])[].scan.ics.calendars[].components[].description).intents[].nameequalscallback_scam
ml.nlu_classifier(strings.parse_html(file.explode(attachments[])[].scan.ics.calendars[].components[].description).display_text).intents[].nameequalscallback_scam
ml.nlu_classifier(file.explode(attachments[])[].scan.ics.calendars[].components[].description).topics[].nameequalsRequest to View Invoice
ml.nlu_classifier(file.explode(attachments[])[].scan.ics.calendars[].components[].description).topics[].confidenceequalshigh
regex.containsregex[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{FE00}-\x{FE0F}\x{200D}\x{20E3}\x{E0020}-\x{E007F}]

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        and
          headers.auth_summary.dmarc.pass eq "true"
          headers.auth_summary.dmarc.pass is_not_null
      sender.email.domain.root_domain macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
    sender.email.domain.root_domain macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  not
    profile.by_sender_email func_call "profile.by_sender_email().any_messages_benign"
  not
    profile.by_sender_email func_call "profile.by_sender_email().solicited"
  any(attachments)
     macro "any(file.explode(.),\n            any(.scan.ics.calendars,\n                any(.components,\n                    any(ml.nlu_classifier(.summary).intents,\n                        .name == \"callback_scam\" and .confidence == \"high\"\n                    )\n                    or (\n                      any(ml.nlu_classifier(.description).intents,\n                          .name == \"callback_scam\"\n                      )\n                      or any(ml.nlu_classifier(strings.parse_html(.description).display_text\n                             ).intents,\n                             .name == \"callback_scam\"\n                      )\n                      or (\n                        any(ml.nlu_classifier(.description).topics,\n                            .name == \"Request to View Invoice\"\n                            and .confidence == \"high\"\n                        )\n                        // emoji regex\n                        and regex.contains(.description,\n                                           '[\\x{1F600}-\\x{1F64F}\\x{1F300}-\\x{1F5FF}\\x{1F680}-\\x{1F6FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{FE00}-\\x{FE0F}\\x{200D}\\x{20E3}\\x{E0020}-\\x{E007F}]'\n                        )\n                      )\n                    )\n                )\n            )\n        )"
  attachments length_compare "0"
  type.inbound eq "true"
   macro "all(attachments, .content_type in (\"text/calendar\", \"application/ics\"))"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via DocuSign comment

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

This rule inspects messages originating from legitimate DocuSign infrastructure, with a DocuSign logo that match Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesEvasion, Impersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and length(attachments) == 0

// Legitimate Docusign sending infratructure
and sender.email.domain.root_domain == 'docusign.net'
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)

// Docusign Logo 
and any(ml.logo_detect(file.message_screenshot()).brands, .name == "DocuSign")

// Callback Phishing
and regex.icontains(body.current_thread.text,
                    (
                      "mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck"
                    )
)
and 3 of (
  strings.ilike(body.current_thread.text, '*purchase*'),
  strings.ilike(body.current_thread.text, '*payment*'),
  strings.ilike(body.current_thread.text, '*transaction*'),
  strings.ilike(body.current_thread.text, '*subscription*'),
  strings.ilike(body.current_thread.text, '*antivirus*'),
  strings.ilike(body.current_thread.text, '*order*'),
  strings.ilike(body.current_thread.text, '*support*'),
  strings.ilike(body.current_thread.text, '*help line*'),
  strings.ilike(body.current_thread.text, '*receipt*'),
  strings.ilike(body.current_thread.text, '*invoice*'),
  strings.ilike(body.current_thread.text, '*call*'),
  strings.ilike(body.current_thread.text, '*cancel*'),
  strings.ilike(body.current_thread.text, '*renew*'),
  strings.ilike(body.current_thread.text, '*refund*')
)
// phone number regex
and any([body.current_thread.text, subject.subject],
        regex.icontains(.,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
        )
)

Detection Logic

Scope: inbound message.

This rule inspects messages originating from legitimate DocuSign infrastructure, with a DocuSign logo that match Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

  1. inbound message
  2. length(attachments) == 0
  3. sender.email.domain.root_domain is 'docusign.net'
  4. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  5. any(ml.logo_detect(file.message_screenshot()).brands, .name == "DocuSign")
  6. body.current_thread.text matches 'mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck'
  7. at least 3 of 14: body.current_thread.text matches any of 14 patterns
    • *purchase*
    • *payment*
    • *transaction*
    • *subscription*
    • *antivirus*
    • *order*
    • *support*
    • *help line*
    • *receipt*
    • *invoice*
    • *call*
    • *cancel*
    • *renew*
    • *refund*
  8. any([body.current_thread.text, subject.subject], regex.icontains(., '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' ) )

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: file.message_screenshot, ml.logo_detect, regex.icontains, strings.ilike.

Indicators Matched (19)

FieldMatchValue
sender.email.domain.root_domainequalsdocusign.net
ml.logo_detect(file.message_screenshot()).brands[].nameequalsDocuSign
regex.icontainsregexmcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
7 more
strings.ilikesubstring*invoice*
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  attachments length_compare "0"
  body.current_thread.text regex_match "mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck"
  sender.email.domain.root_domain eq "docusign.net"
  type.inbound eq "true"
   macro "3 of (\n  strings.ilike(body.current_thread.text, '*purchase*'),\n  strings.ilike(body.current_thread.text, '*payment*'),\n  strings.ilike(body.current_thread.text, '*transaction*'),\n  strings.ilike(body.current_thread.text, '*subscription*'),\n  strings.ilike(body.current_thread.text, '*antivirus*'),\n  strings.ilike(body.current_thread.text, '*order*'),\n  strings.ilike(body.current_thread.text, '*support*'),\n  strings.ilike(body.current_thread.text, '*help line*'),\n  strings.ilike(body.current_thread.text, '*receipt*'),\n  strings.ilike(body.current_thread.text, '*invoice*'),\n  strings.ilike(body.current_thread.text, '*call*'),\n  strings.ilike(body.current_thread.text, '*cancel*'),\n  strings.ilike(body.current_thread.text, '*renew*'),\n  strings.ilike(body.current_thread.text, '*refund*')\n)"
   macro "any([body.current_thread.text, subject.subject],\n        regex.icontains(.,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n        )\n)"
   macro "any(ml.logo_detect(file.message_screenshot()).brands, .name == \"DocuSign\")"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
    • mcafee
    • n[o0]rt[o0]n
    • geek.{0,5}squad
    • paypal
    • ebay
    • symantec
    • best buy
    • lifel[o0]ck
    corpus 7 (mql 7)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
sender.email.domain.root_domaineq
  • docusign.net corpus 5 (mql 5)
field:"sender.email.domain.root_domain" kind:eq value:"docusign.net"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via e-signature service

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

Detects messages containing e-signature topics combined with tech support keywords and phone numbers. Message includes brand impersonation (PayPal, Norton, McAfee, etc.) and transaction-related language, with no attachments and reply-to addresses from free email providers.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesFree email provider, Impersonation: Brand, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • headers.reply_to
  • type

Rule Body

type.inbound
and length(attachments) == 0
and any(headers.reply_to, .email.domain.root_domain in $free_email_providers)
and any(beta.ml_topic(body.current_thread.text).topics, .name == "E-Signature")
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
and (
  // this section is synced with attachment_callback_phish_with_pdf.yml and attachment_callback_phish_with_img.yml
  regex.icontains(strings.replace_confusables(body.current_thread.text),
                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in (
           "PayPal",
           "Norton",
           "GeekSquad",
           "Ebay",
           "McAfee",
           "AT&T",
           "Microsoft"
         )
  )
)
and (
  (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // however, the 3 of logic and requiring a phone number is specific to this rule in order to reduce FPs
    // caused by messages which mention cancelling or otherwise managing a subscription
    // it is also synced and below for message_screenshot OCR output
    3 of (
      strings.icontains(body.current_thread.text, 'purchase'),
      strings.icontains(body.current_thread.text, 'payment'),
      strings.icontains(body.current_thread.text, 'transaction'),
      strings.icontains(body.current_thread.text, 'subscription'),
      strings.icontains(body.current_thread.text, 'antivirus'),
      strings.icontains(body.current_thread.text, 'order'),
      strings.icontains(body.current_thread.text, 'support'),
      strings.icontains(body.current_thread.text, 'help line'),
      strings.icontains(body.current_thread.text, 'receipt'),
      strings.icontains(body.current_thread.text, 'invoice'),
      strings.icontains(body.current_thread.text, 'call'),
      strings.icontains(body.current_thread.text, 'cancel'),
      strings.icontains(body.current_thread.text, 'renew'),
      strings.icontains(body.current_thread.text, 'refund'),
      regex.icontains(body.current_thread.text, "(?:reach|contact) us at"),
      strings.icontains(body.current_thread.text, "+1"),
      strings.icontains(body.current_thread.text, "amount"),
      strings.icontains(body.current_thread.text, "charged"),
      strings.icontains(body.current_thread.text, "crypto"),
      strings.icontains(body.current_thread.text, "wallet address"),
      regex.icontains(body.current_thread.text, '\$\d{3}\.\d{2}\b'),
    )
    // phone number regex
    and regex.icontains(body.current_thread.text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )
  )
  or (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // and above for current_thread.text
    //
    // 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
    //
    3 of (
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'subscription'
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      "(?:reach|contact) us at"
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'wallet address'
      ),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      '\$\d{3}\.\d{2}\b'
      ),
    )
    // phone number regex
    and regex.icontains(beta.ocr(file.message_screenshot()).text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )

    // negate messages with previous threads.  While callback phishing with thread hijacking or with current_thread 
    // padded with whitespace and previous threads in the message has been observed, the intetion of using OCR is for image embedded callbacks
    and not regex.icount(beta.ocr(file.message_screenshot()).text,
                         '(?:from|to|sent|date|cc|subject):'
    ) > 3
    // this notation of previous threads often only occurs once
    and not regex.icontains(beta.ocr(file.message_screenshot()).text,
                            'wrote:[\r\n]'
    )
  )
)

Detection Logic

Scope: inbound message.

Detects messages containing e-signature topics combined with tech support keywords and phone numbers. Message includes brand impersonation (PayPal, Norton, McAfee, etc.) and transaction-related language, with no attachments and reply-to addresses from free email providers.

  1. inbound message
  2. length(attachments) == 0
  3. any of headers.reply_to where:
    • .email.domain.root_domain in $free_email_providers
  4. any(beta.ml_topic(body.current_thread.text).topics, .name == "E-Signature")
  5. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  6. any of:
    • regex.icontains(strings.replace_confusables(body.current_thread.text), '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)' )
    • any(ml.logo_detect(file.message_screenshot()).brands, .name in ( "PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T", "Microsoft" ) )
  7. any of:
    • all of:
      • at least 3 of:
        • body.current_thread.text contains 'purchase'
        • body.current_thread.text contains 'payment'
        • body.current_thread.text contains 'transaction'
        • body.current_thread.text contains 'subscription'
        • body.current_thread.text contains 'antivirus'
        • body.current_thread.text contains 'order'
        • body.current_thread.text contains 'support'
        • body.current_thread.text contains 'help line'
        • body.current_thread.text contains 'receipt'
        • body.current_thread.text contains 'invoice'
        • body.current_thread.text contains 'call'
        • body.current_thread.text contains 'cancel'
        • body.current_thread.text contains 'renew'
        • body.current_thread.text contains 'refund'
        • body.current_thread.text matches '(?:reach|contact) us at'
        • body.current_thread.text contains '+1'
        • body.current_thread.text contains 'amount'
        • body.current_thread.text contains 'charged'
        • body.current_thread.text contains 'crypto'
        • body.current_thread.text contains 'wallet address'
        • body.current_thread.text matches '\\$\\d{3}\\.\\d{2}\\b'
      • body.current_thread.text matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
    • all of:
      • at least 3 of:
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'subscription' )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'order')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'support')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'call')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund')
        • regex.icontains(beta.ocr(file.message_screenshot()).text, "(?:reach|contact) us at" )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, '+1')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'wallet address' )
        • regex.icontains(beta.ocr(file.message_screenshot()).text, '\$\d{3}\.\d{2}\b' )
      • regex.icontains(beta.ocr(file.message_screenshot()).text, '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' )
      • not:
        • regex.icount(beta.ocr(file.message_screenshot()).text, '(?:from|to|sent|date|cc|subject):' ) > 3
      • not:
        • regex.icontains(beta.ocr(file.message_screenshot()).text, 'wrote:[\r\n]' )

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.reply_to, headers.reply_to[].email.domain.root_domain, type.inbound. Sensors: beta.ml_topic, beta.ocr, file.message_screenshot, ml.logo_detect, regex.icontains, regex.icount, strings.icontains, strings.replace_confusables. Reference lists: $free_email_providers.

Indicators Matched (33)

FieldMatchValue
beta.ml_topic(body.current_thread.text).topics[].nameequalsE-Signature
regex.icontainsregex(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
ml.logo_detect(file.message_screenshot()).brands[].namememberMcAfee
ml.logo_detect(file.message_screenshot()).brands[].namememberAT&T
ml.logo_detect(file.message_screenshot()).brands[].namememberMicrosoft
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
21 more
strings.icontainssubstringsubscription
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
regex.icontainsregex(?:reach|contact) us at
strings.icontainssubstring+1
strings.icontainssubstringamount
strings.icontainssubstringcharged
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringhelpdesk

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
        body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
       macro "3 of (\n      strings.icontains(body.current_thread.text, 'purchase'),\n      strings.icontains(body.current_thread.text, 'payment'),\n      strings.icontains(body.current_thread.text, 'transaction'),\n      strings.icontains(body.current_thread.text, 'subscription'),\n      strings.icontains(body.current_thread.text, 'antivirus'),\n      strings.icontains(body.current_thread.text, 'order'),\n      strings.icontains(body.current_thread.text, 'support'),\n      strings.icontains(body.current_thread.text, 'help line'),\n      strings.icontains(body.current_thread.text, 'receipt'),\n      strings.icontains(body.current_thread.text, 'invoice'),\n      strings.icontains(body.current_thread.text, 'call'),\n      strings.icontains(body.current_thread.text, 'cancel'),\n      strings.icontains(body.current_thread.text, 'renew'),\n      strings.icontains(body.current_thread.text, 'refund'),\n      regex.icontains(body.current_thread.text, \"(?:reach|contact) us at\"),\n      strings.icontains(body.current_thread.text, \"+1\"),\n      strings.icontains(body.current_thread.text, \"amount\"),\n      strings.icontains(body.current_thread.text, \"charged\"),\n      strings.icontains(body.current_thread.text, \"crypto\"),\n      strings.icontains(body.current_thread.text, \"wallet address\"),\n      regex.icontains(body.current_thread.text, '\\$\\d{3}\\.\\d{2}\\b'),\n    )"
    and
      not
        regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                            'wrote:[\\r\\n]'\n    )"
      not
        regex.icount func_call "regex.icount(beta.ocr(file.message_screenshot()).text,\n                         '(?:from|to|sent|date|cc|subject):'\n    ) > 3"
      regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n    )"
       macro "3 of (\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'subscription'\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      \"(?:reach|contact) us at\"\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'wallet address'\n      ),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      '\\$\\d{3}\\.\\d{2}\\b'\n      ),\n    )"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  any(headers.reply_to)
    headers.reply_to.email.domain.root_domain macro "headers.reply_to[].email.domain.root_domain in free_email_providers"
  or
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'\n  )"
     macro "any(ml.logo_detect(file.message_screenshot()).brands,\n         .name in (\n           \"PayPal\",\n           \"Norton\",\n           \"GeekSquad\",\n           \"Ebay\",\n           \"McAfee\",\n           \"AT&T\",\n           \"Microsoft\"\n         )\n  )"
  attachments length_compare "0"
  type.inbound eq "true"
   macro "any(beta.ml_topic(body.current_thread.text).topics, .name == \"E-Signature\")"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via extensionless rfc822 attachment

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

This rule detects messages with an unknown file_type (extensionless) and a content_type of 'message/rfc822' containing an image file with Callback Phishing indicators.

Threat Classification

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

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

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and any(attachments,
        .file_type == "unknown"
        and (.content_type == "message/rfc822" or .file_extension in ('eml'))
        and any(file.explode(.),
                strings.starts_with(.flavors.mime, "image")

                // and Image contains callback phishing indicators
                and 4 of (
                  strings.icontains(.scan.ocr.raw, "purchase"),
                  strings.icontains(.scan.ocr.raw, "subscription"),
                  strings.icontains(.scan.ocr.raw, "antivirus"),
                  strings.icontains(.scan.ocr.raw, "order"),
                  strings.icontains(.scan.ocr.raw, "support"),
                  strings.icontains(.scan.ocr.raw, "receipt"),
                  strings.icontains(.scan.ocr.raw, "amount"),
                  strings.icontains(.scan.ocr.raw, "charged"),
                  strings.icontains(.scan.ocr.raw, "invoice"),
                  strings.icontains(.scan.ocr.raw, "call"),
                  strings.icontains(.scan.ocr.raw, "cancel"),
                  strings.icontains(.scan.ocr.raw, "renew"),
                  strings.icontains(.scan.ocr.raw, "refund"),
                  strings.icontains(.scan.ocr.raw, "+1")
                )
        )
        and any(file.explode(.),
                strings.ilike(.scan.ocr.raw,
                              "*geek*squad*",
                              "*lifelock*",
                              "*best buy*",
                              "*mcafee*",
                              "*norton*",
                              "*ebay*",
                              "*paypal*",
                              "*secure anywhere*"
                )
                or any(ml.nlu_classifier(.scan.ocr.raw).intents,
                       .name in ("callback_scam")
                       and .confidence == "high"
                       and length(body.current_thread.text) < 1500
                )
        )
)
and (
  not profile.by_sender().solicited
  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.

This rule detects messages with an unknown file_type (extensionless) and a content_type of 'message/rfc822' containing an image file with Callback Phishing indicators.

  1. inbound message
  2. any of attachments where all hold:
    • .file_type is 'unknown'
    • any of:
      • .content_type is 'message/rfc822'
      • .file_extension in ('eml')
    • any(file.explode(.), strings.starts_with(.flavors.mime, "image") // and Image contains callback phishing indicators and 4 of ( strings.icontains(.scan.ocr.raw, "purchase"), strings.icontains(.scan.ocr.raw, "subscription"), strings.icontains(.scan.ocr.raw, "antivirus"), strings.icontains(.scan.ocr.raw, "order"), strings.icontains(.scan.ocr.raw, "support"), strings.icontains(.scan.ocr.raw, "receipt"), strings.icontains(.scan.ocr.raw, "amount"), strings.icontains(.scan.ocr.raw, "charged"), strings.icontains(.scan.ocr.raw, "invoice"), strings.icontains(.scan.ocr.raw, "call"), strings.icontains(.scan.ocr.raw, "cancel"), strings.icontains(.scan.ocr.raw, "renew"), strings.icontains(.scan.ocr.raw, "refund"), strings.icontains(.scan.ocr.raw, "+1") ) )
    • any(file.explode(.), strings.ilike(.scan.ocr.raw, "*geek*squad*", "*lifelock*", "*best buy*", "*mcafee*", "*norton*", "*ebay*", "*paypal*", "*secure anywhere*" ) or any(ml.nlu_classifier(.scan.ocr.raw).intents, .name in ("callback_scam") and .confidence == "high" and length(body.current_thread.text) < 1500 ) )
  3. all of:
    • not:
      • profile.by_sender().solicited
    • not:
      • profile.by_sender().any_messages_benign
  4. 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[].content_type, attachments[].file_extension, attachments[].file_type, body.current_thread.text, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, type.inbound. Sensors: file.explode, ml.nlu_classifier, profile.by_sender, strings.icontains, strings.ilike, strings.starts_with. Reference lists: $high_trust_sender_root_domains.

Indicators Matched (28)

FieldMatchValue
attachments[].file_typeequalsunknown
attachments[].content_typeequalsmessage/rfc822
attachments[].file_extensionmembereml
strings.starts_withprefiximage
strings.icontainssubstringpurchase
strings.icontainssubstringsubscription
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringreceipt
strings.icontainssubstringamount
strings.icontainssubstringcharged
16 more
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
strings.icontainssubstring+1
strings.ilikesubstring*geek*squad*
strings.ilikesubstring*lifelock*
strings.ilikesubstring*best buy*
strings.ilikesubstring*mcafee*
strings.ilikesubstring*norton*
strings.ilikesubstring*ebay*
strings.ilikesubstring*paypal*
strings.ilikesubstring*secure anywhere*
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].namemembercallback_scam
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  any(attachments)
    and
      or
        attachments.content_type eq "message/rfc822"
        attachments.file_extension eq "eml"
      attachments.file_type eq "unknown"
       macro "any(file.explode(.),\n                strings.ilike(.scan.ocr.raw,\n                              \"*geek*squad*\",\n                              \"*lifelock*\",\n                              \"*best buy*\",\n                              \"*mcafee*\",\n                              \"*norton*\",\n                              \"*ebay*\",\n                              \"*paypal*\",\n                              \"*secure anywhere*\"\n                )\n                or any(ml.nlu_classifier(.scan.ocr.raw).intents,\n                       .name in (\"callback_scam\")\n                       and .confidence == \"high\"\n                       and length(body.current_thread.text) < 1500\n                )\n        )"
       macro "any(file.explode(.),\n                strings.starts_with(.flavors.mime, \"image\")\n\n                // and Image contains callback phishing indicators\n                and 4 of (\n                  strings.icontains(.scan.ocr.raw, \"purchase\"),\n                  strings.icontains(.scan.ocr.raw, \"subscription\"),\n                  strings.icontains(.scan.ocr.raw, \"antivirus\"),\n                  strings.icontains(.scan.ocr.raw, \"order\"),\n                  strings.icontains(.scan.ocr.raw, \"support\"),\n                  strings.icontains(.scan.ocr.raw, \"receipt\"),\n                  strings.icontains(.scan.ocr.raw, \"amount\"),\n                  strings.icontains(.scan.ocr.raw, \"charged\"),\n                  strings.icontains(.scan.ocr.raw, \"invoice\"),\n                  strings.icontains(.scan.ocr.raw, \"call\"),\n                  strings.icontains(.scan.ocr.raw, \"cancel\"),\n                  strings.icontains(.scan.ocr.raw, \"renew\"),\n                  strings.icontains(.scan.ocr.raw, \"refund\"),\n                  strings.icontains(.scan.ocr.raw, \"+1\")\n                )\n        )"
  or
    and
      not
        headers.auth_summary.dmarc.pass eq "true"
      sender.email.domain.root_domain macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
    sender.email.domain.root_domain macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via Google Group abuse

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

A fraudulent invoice/receipt found in the body of the message, delivered via a Google Group mailing list.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesFree email provider, Impersonation: Brand, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) < 5
and sender.email.domain.domain == "googlegroups.com"
and (
  any(attachments,
      (.file_type in $file_types_images or .file_type == "pdf")
      and (
        any(file.explode(.),
            // exclude images taken with mobile cameras and screenshots from android
            not any(.scan.exiftool.fields,
                    (
                      .key == "Model"
                      or (
                        .key == "Software"
                        and strings.starts_with(.value, "Android")
                      )
                    )
                    // exclude images taken with mobile cameras and screenshots from Apple
                    and (
                      .key == "DeviceManufacturer"
                      and .value == "Apple Computer Inc."
                    )
            )
            and any(ml.nlu_classifier(.scan.ocr.raw).intents,
                    .name == "callback_scam" and .confidence == "high"
            )
        )
      )
  )
  or any(ml.nlu_classifier(body.current_thread.text).intents,
         .name in ("callback_scam") and .confidence == "high"
  )
)

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

A fraudulent invoice/receipt found in the body of the message, delivered via a Google Group mailing list.

  1. inbound message
  2. length(attachments) < 5
  3. sender.email.domain.domain is 'googlegroups.com'
  4. any of:
    • any of attachments where all hold:
      • any of:
        • .file_type in $file_types_images
        • .file_type is 'pdf'
      • ( any(file.explode(.), // exclude images taken with mobile cameras and screenshots from android not any(.scan.exiftool.fields, ( .key == "Model" or ( .key == "Software" and strings.starts_with(.value, "Android") ) ) // exclude images taken with mobile cameras and screenshots from Apple and ( .key == "DeviceManufacturer" and .value == "Apple Computer Inc." ) ) and any(ml.nlu_classifier(.scan.ocr.raw).intents, .name == "callback_scam" and .confidence == "high" ) ) )
    • any(ml.nlu_classifier(body.current_thread.text).intents, .name in ("callback_scam") and .confidence == "high" )
  5. 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_type, body.current_thread.text, headers.auth_summary.dmarc.pass, sender.email.domain.domain, sender.email.domain.root_domain, type.inbound. Sensors: file.explode, ml.nlu_classifier, strings.starts_with. Reference lists: $file_types_images, $high_trust_sender_root_domains.

Indicators Matched (6)

FieldMatchValue
sender.email.domain.domainequalsgooglegroups.com
attachments[].file_typeequalspdf
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].nameequalscallback_scam
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).intents[].confidenceequalshigh
ml.nlu_classifier(body.current_thread.text).intents[].namemembercallback_scam
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(attachments)
      and
        or
          attachments.file_type eq "pdf"
          attachments.file_type macro "attachments[].file_type in file_types_images"
         macro "(\n        any(file.explode(.),\n            // exclude images taken with mobile cameras and screenshots from android\n            not any(.scan.exiftool.fields,\n                    (\n                      .key == \"Model\"\n                      or (\n                        .key == \"Software\"\n                        and strings.starts_with(.value, \"Android\")\n                      )\n                    )\n                    // exclude images taken with mobile cameras and screenshots from Apple\n                    and (\n                      .key == \"DeviceManufacturer\"\n                      and .value == \"Apple Computer Inc.\"\n                    )\n            )\n            and any(ml.nlu_classifier(.scan.ocr.raw).intents,\n                    .name == \"callback_scam\" and .confidence == \"high\"\n            )\n        )\n      )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n         .name in (\"callback_scam\") and .confidence == \"high\"\n  )"
  or
    and
      not
        headers.auth_summary.dmarc.pass eq "true"
      sender.email.domain.root_domain macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
    sender.email.domain.root_domain macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  attachments length_compare "5"
  sender.email.domain.domain eq "googlegroups.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Callback phishing via Google Meet

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

Detects Google Meet invitations that include callback phishing content in the body, impersonating brands like McAfee, Norton, PayPal, or Best Buy with purchase-related language and phone numbers to solicit victim contact.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesOut of band pivot

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • body.links
  • subject
  • type

Rule Body

type.inbound
and length(attachments) == 2
and all(attachments,
        .content_type == "text/calendar" or .file_extension == "ics"
)
and strings.starts_with(subject.base, "Invitation:")
// Google Meet indicators
and (
  any(body.links,
      .display_text == "Join with Google Meet"
      and .href_url.domain.domain == "meet.google.com"
  )
)
and (
  (
    regex.icontains(body.current_thread.text,
                    (
                      "(?:mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock|apple)"
                    )
    )
    and 3 of (
      strings.ilike(body.current_thread.text, '*purchase*'),
      strings.ilike(body.current_thread.text, '*payment*'),
      strings.ilike(body.current_thread.text, '*transaction*'),
      strings.ilike(body.current_thread.text, '*subscription*'),
      strings.ilike(body.current_thread.text, '*antivirus*'),
      strings.ilike(body.current_thread.text, '*order*'),
      strings.ilike(body.current_thread.text, '*support*'),
      strings.ilike(body.current_thread.text, '*help line*'),
      strings.ilike(body.current_thread.text, '*receipt*'),
      strings.ilike(body.current_thread.text, '*invoice*'),
      strings.ilike(body.current_thread.text, '*call*'),
      strings.ilike(body.current_thread.text, '*cancel*'),
      strings.ilike(body.current_thread.text, '*renew*'),
      strings.ilike(body.current_thread.text, '*refund*')
    )
    // toll-free phone number regex
    and any([body.current_thread.text, subject.subject],
            regex.icontains(., '\b\+?(\d{1}.)?\(?8\d{2}?\)?.\d{3}.?\d{4}\b')
    )
  )
)

Detection Logic

Scope: inbound message.

Detects Google Meet invitations that include callback phishing content in the body, impersonating brands like McAfee, Norton, PayPal, or Best Buy with purchase-related language and phone numbers to solicit victim contact.

  1. inbound message
  2. length(attachments) == 2
  3. all of attachments where any holds:
    • .content_type is 'text/calendar'
    • .file_extension is 'ics'
  4. subject.base starts with 'Invitation:'
  5. any of body.links where all hold:
    • .display_text is 'Join with Google Meet'
    • .href_url.domain.domain is 'meet.google.com'
  6. all of:
    • body.current_thread.text matches '(?:mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock|apple)'
    • at least 3 of 14: body.current_thread.text matches any of 14 patterns
      • *purchase*
      • *payment*
      • *transaction*
      • *subscription*
      • *antivirus*
      • *order*
      • *support*
      • *help line*
      • *receipt*
      • *invoice*
      • *call*
      • *cancel*
      • *renew*
      • *refund*
    • any([body.current_thread.text, subject.subject], regex.icontains(., '\b\+?(\d{1}.)?\(?8\d{2}?\)?.\d{3}.?\d{4}\b') )

Inspects: attachments[].content_type, attachments[].file_extension, body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.domain.domain, subject.base, subject.subject, type.inbound. Sensors: regex.icontains, strings.ilike, strings.starts_with.

Indicators Matched (21)

FieldMatchValue
attachments[].content_typeequalstext/calendar
attachments[].file_extensionequalsics
strings.starts_withprefixInvitation:
body.links[].display_textequalsJoin with Google Meet
body.links[].href_url.domain.domainequalsmeet.google.com
regex.icontainsregex(?:mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock|apple)
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
9 more
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
strings.ilikesubstring*invoice*
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
regex.icontainsregex\b\+?(\d{1}.)?\(?8\d{2}?\)?.\d{3}.?\d{4}\b

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      body.links.display_text eq "Join with Google Meet"
      body.links.href_url.domain.domain eq "meet.google.com"
  attachments length_compare "2"
  body.current_thread.text regex_match "(?:mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock|apple)"
  subject.base starts_with "Invitation:"
  type.inbound eq "true"
   macro "3 of (\n      strings.ilike(body.current_thread.text, '*purchase*'),\n      strings.ilike(body.current_thread.text, '*payment*'),\n      strings.ilike(body.current_thread.text, '*transaction*'),\n      strings.ilike(body.current_thread.text, '*subscription*'),\n      strings.ilike(body.current_thread.text, '*antivirus*'),\n      strings.ilike(body.current_thread.text, '*order*'),\n      strings.ilike(body.current_thread.text, '*support*'),\n      strings.ilike(body.current_thread.text, '*help line*'),\n      strings.ilike(body.current_thread.text, '*receipt*'),\n      strings.ilike(body.current_thread.text, '*invoice*'),\n      strings.ilike(body.current_thread.text, '*call*'),\n      strings.ilike(body.current_thread.text, '*cancel*'),\n      strings.ilike(body.current_thread.text, '*renew*'),\n      strings.ilike(body.current_thread.text, '*refund*')\n    )"
   macro "all(attachments,\n        .content_type == \"text/calendar\" or .file_extension == \"ics\"\n)"
   macro "any([body.current_thread.text, subject.subject],\n            regex.icontains(., '\\b\\+?(\\d{1}.)?\\(?8\\d{2}?\\)?.\\d{3}.?\\d{4}\\b')\n    )"

Indicators

These rows show field, operator, and value matches.

Callback phishing via Intuit service abuse

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

Callback phishing campaigns have been observed abusing Intuit Quickbooks services to send fraudulent invoices with callback phishing contents.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesEvasion, Free email provider, Impersonation: Brand, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • body.html
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule Body

type.inbound

// Legitimate Intuit sending infratructure
and (
  sender.email.domain.root_domain in ('intuit.com', 'intuit.co.uk')
  // check for SPF or DMARC passed
  and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
)
and (
  // Callback Phishing in body (brand names required)
  (
    length(attachments) == 0

    // brand names are required.
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        (
                          "mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock"
                        )
    )
    and 3 of (
      strings.ilike(body.current_thread.text, '*purchase*'),
      strings.ilike(body.current_thread.text, '*payment*'),
      strings.ilike(body.current_thread.text, '*transaction*'),
      strings.ilike(body.current_thread.text, '*subscription*'),
      strings.ilike(body.current_thread.text, '*antivirus*'),
      strings.ilike(body.current_thread.text, '*order*'),
      strings.ilike(body.current_thread.text, '*support*'),
      strings.ilike(body.current_thread.text, '*help line*'),
      strings.ilike(body.current_thread.text, '*receipt*'),
      strings.ilike(body.current_thread.text, '*invoice*'),
      strings.ilike(body.current_thread.text, '*call*'),
      strings.ilike(body.current_thread.text, '*cancel*'),
      strings.ilike(body.current_thread.text, '*renew*'),
      strings.ilike(body.current_thread.text, '*refund*')
    )
    // phone number regex
    and any([body.current_thread.text, subject.subject],
            regex.icontains(., '\b\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}\b')
    )
  )
  // Callback Phishing in the "billtoContent"
  or 
  // icontains a phone number
  (
    regex.icontains(strings.replace_confusables(body.html.inner_text),
                    '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}.*\n'
    )
    or regex.icontains(strings.replace_confusables(body.html.inner_text),
                       '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\+\d{1,3}[ilo0-9]{10}.*\n'
    )
    // +12028001238
    or regex.icontains(strings.replace_confusables(body.html.inner_text),
                       '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\n'
    )
    // 202-800-1238
    or regex.icontains(strings.replace_confusables(body.html.inner_text),
                       '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\n'
    )
    // (202) 800-1238
    or regex.icontains(strings.replace_confusables(body.html.inner_text),
                       '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*\n'
    )
    // (202)-800-1238
    or regex.icontains(strings.replace_confusables(body.html.inner_text),
                       '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\n'
    )
    // 202 800 1238
    or regex.icontains(strings.replace_confusables(body.html.inner_text),
                       '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+1\s?[ilo0-9]{3} [ilo0-9]{3} [ilo0-9]{4}.*\n'
    ) // 8123456789
    or (
      regex.icontains(strings.replace_confusables(body.html.inner_text),
                      '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+8\d{9}.*\n'
      )
      and regex.icontains(strings.replace_confusables(body.html.inner_text),
                          '\+1'
      )
    )
  )
  // all attachments are PDFs with callback phishing indicators Brands Required
  or (
    length(attachments) < 3
    and all(attachments, .file_extension == "pdf")
    // the attachment is a pdf with 1 page, and at least 60 ocr chars
    and any(attachments,
            (
              .file_extension == "pdf"
              and any(file.explode(.), .scan.exiftool.page_count < 3)
              and any(file.explode(.), length(.scan.ocr.raw) > 60)

              // 4 of the following strings are found
              and (
                any(file.explode(.),
                    4 of (
                      strings.icontains(.scan.ocr.raw, "purchase"),
                      strings.icontains(.scan.ocr.raw, "payment"),
                      strings.icontains(.scan.ocr.raw, "transaction"),
                      strings.icontains(.scan.ocr.raw, "subscription"),
                      strings.icontains(.scan.ocr.raw, "antivirus"),
                      strings.icontains(.scan.ocr.raw, "order"),
                      strings.icontains(.scan.ocr.raw, "support"),
                      strings.icontains(.scan.ocr.raw, "help line"),
                      strings.icontains(.scan.ocr.raw, "receipt"),
                      strings.icontains(.scan.ocr.raw, "invoice"),
                      strings.icontains(.scan.ocr.raw, "call"),
                      strings.icontains(.scan.ocr.raw, "helpdesk"),
                      strings.icontains(.scan.ocr.raw, "cancel"),
                      strings.icontains(.scan.ocr.raw, "renew"),
                      strings.icontains(.scan.ocr.raw, "refund"),
                      strings.icontains(.scan.ocr.raw, "amount"),
                      strings.icontains(.scan.ocr.raw, "crypto"),
                      strings.icontains(.scan.ocr.raw, "wallet address"),
                      regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b'),
                      regex.icontains(.scan.ocr.raw,
                                      '(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})'
                      ),
                      regex.icontains(.scan.ocr.raw,
                                      '\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}'
                      )
                    )

                    // 1 of the following strings is found, representing common Callback brands
                    and (
                      1 of (
                        strings.icontains(.scan.ocr.raw, "geek squad"),
                        strings.icontains(.scan.ocr.raw, "lifelock"),
                        strings.icontains(.scan.ocr.raw, "best buy"),
                        strings.icontains(.scan.ocr.raw, "mcafee"),
                        strings.icontains(.scan.ocr.raw, "norton"),
                        strings.icontains(.scan.ocr.raw, "ebay"),
                        strings.icontains(.scan.ocr.raw, "paypal"),
                      )
                      // add additional logic for common language for paypal, which is a valid payment method
                      and not regex.icontains(.scan.ocr.raw,
                                              "paypal[^\n]+accepted"
                      )
                      and not regex.icontains(.scan.ocr.raw,
                                              "pay(ment)?.{0,30}(via|by) paypal"
                      )
                      and not regex.icontains(.scan.ocr.raw,
                                              '\d{2,4} norton'
                      ) // an address, example: 1234 Norton Road
                      and not regex.icontains(.scan.ocr.raw,
                                              'sold on.{0,20}ebay'
                      ) // "cannot be sold on Amazon and EBay", from a legit vendor
                    )
                )
                or any(ml.logo_detect(.).brands,
                       .name in ("PayPal", "Norton", "GeekSquad", "Ebay")
                )
              )
            )
    )
  )
)

//
// 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 (
  not beta.profile.by_reply_to().any_messages_benign
  or length(headers.reply_to) == 0
)

Detection Logic

Scope: inbound message.

Callback phishing campaigns have been observed abusing Intuit Quickbooks services to send fraudulent invoices with callback phishing contents.

  1. inbound message
  2. all of:
    • sender.email.domain.root_domain in ('intuit.com', 'intuit.co.uk')
    • any of:
      • headers.auth_summary.spf.pass
      • headers.auth_summary.dmarc.pass
  3. any of:
    • all of:
      • length(attachments) == 0
      • regex.icontains(strings.replace_confusables(body.current_thread.text), ( "mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock" ) )
      • at least 3 of 14: body.current_thread.text matches any of 14 patterns
        • *purchase*
        • *payment*
        • *transaction*
        • *subscription*
        • *antivirus*
        • *order*
        • *support*
        • *help line*
        • *receipt*
        • *invoice*
        • *call*
        • *cancel*
        • *renew*
        • *refund*
      • any([body.current_thread.text, subject.subject], regex.icontains(., '\b\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}\b') )
    • any of:
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\+\d{1,3}[ilo0-9]{10}.*\n' )
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+1\s?[ilo0-9]{3} [ilo0-9]{3} [ilo0-9]{4}.*\n' )
      • all of:
        • regex.icontains(strings.replace_confusables(body.html.inner_text), '(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+8\d{9}.*\n' )
        • regex.icontains(strings.replace_confusables(body.html.inner_text), '\+1' )
    • all of:
      • length(attachments) < 3
      • all of attachments where:
        • .file_extension is 'pdf'
      • any of attachments where all hold:
        • .file_extension is 'pdf'
        • any(file.explode(.), .scan.exiftool.page_count < 3)
        • any(file.explode(.), length(.scan.ocr.raw) > 60)
        • any of:
          • any(file.explode(.), 4 of ( strings.icontains(.scan.ocr.raw, "purchase"), strings.icontains(.scan.ocr.raw, "payment"), strings.icontains(.scan.ocr.raw, "transaction"), strings.icontains(.scan.ocr.raw, "subscription"), strings.icontains(.scan.ocr.raw, "antivirus"), strings.icontains(.scan.ocr.raw, "order"), strings.icontains(.scan.ocr.raw, "support"), strings.icontains(.scan.ocr.raw, "help line"), strings.icontains(.scan.ocr.raw, "receipt"), strings.icontains(.scan.ocr.raw, "invoice"), strings.icontains(.scan.ocr.raw, "call"), strings.icontains(.scan.ocr.raw, "helpdesk"), strings.icontains(.scan.ocr.raw, "cancel"), strings.icontains(.scan.ocr.raw, "renew"), strings.icontains(.scan.ocr.raw, "refund"), strings.icontains(.scan.ocr.raw, "amount"), strings.icontains(.scan.ocr.raw, "crypto"), strings.icontains(.scan.ocr.raw, "wallet address"), regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b'), regex.icontains(.scan.ocr.raw, '(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})' ), regex.icontains(.scan.ocr.raw, '\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}' ) ) // 1 of the following strings is found, representing common Callback brands and ( 1 of ( strings.icontains(.scan.ocr.raw, "geek squad"), strings.icontains(.scan.ocr.raw, "lifelock"), strings.icontains(.scan.ocr.raw, "best buy"), strings.icontains(.scan.ocr.raw, "mcafee"), strings.icontains(.scan.ocr.raw, "norton"), strings.icontains(.scan.ocr.raw, "ebay"), strings.icontains(.scan.ocr.raw, "paypal"), ) // add additional logic for common language for paypal, which is a valid payment method and not regex.icontains(.scan.ocr.raw, "paypal[^\n]+accepted" ) and not regex.icontains(.scan.ocr.raw, "pay(ment)?.{0,30}(via|by) paypal" ) and not regex.icontains(.scan.ocr.raw, '\d{2,4} norton' ) // an address, example: 1234 Norton Road and not regex.icontains(.scan.ocr.raw, 'sold on.{0,20}ebay' ) // "cannot be sold on Amazon and EBay", from a legit vendor ) )
          • any(ml.logo_detect(.).brands, .name in ("PayPal", "Norton", "GeekSquad", "Ebay") )
  4. any of:
    • not:
      • beta.profile.by_reply_to().any_messages_benign
    • length(headers.reply_to) == 0

Inspects: attachments[].file_extension, body.current_thread.text, body.html.inner_text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.reply_to, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: beta.profile.by_reply_to, file.explode, ml.logo_detect, regex.icontains, strings.icontains, strings.ilike, strings.replace_confusables.

Indicators Matched (60)

FieldMatchValue
sender.email.domain.root_domainmemberintuit.com
sender.email.domain.root_domainmemberintuit.co.uk
regex.icontainsregexmcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
48 more
strings.ilikesubstring*invoice*
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
regex.icontainsregex\b\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}\b
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\+\d{1,3}[ilo0-9]{10}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+1\s?[ilo0-9]{3} [ilo0-9]{3} [ilo0-9]{4}.*\n
regex.icontainsregex(?:Sold|Bill)[\s\xa0]To(?:\:\s+|\n)[^\n]+8\d{9}.*\n
regex.icontainsregex\+1
attachments[].file_extensionequalspdf
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
strings.icontainssubstringsubscription
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringhelpdesk
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
strings.icontainssubstringamount
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})
regex.icontainsregex\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}
strings.icontainssubstringgeek squad
strings.icontainssubstringlifelock
strings.icontainssubstringbest buy
strings.icontainssubstringmcafee
strings.icontainssubstringnorton
strings.icontainssubstringebay
strings.icontainssubstringpaypal
ml.logo_detect(attachments[]).brands[].namememberPayPal
ml.logo_detect(attachments[]).brands[].namememberNorton
ml.logo_detect(attachments[]).brands[].namememberGeekSquad
ml.logo_detect(attachments[]).brands[].namememberEbay

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(attachments)
        and
          or
             macro "any(file.explode(.),\n                    4 of (\n                      strings.icontains(.scan.ocr.raw, \"purchase\"),\n                      strings.icontains(.scan.ocr.raw, \"payment\"),\n                      strings.icontains(.scan.ocr.raw, \"transaction\"),\n                      strings.icontains(.scan.ocr.raw, \"subscription\"),\n                      strings.icontains(.scan.ocr.raw, \"antivirus\"),\n                      strings.icontains(.scan.ocr.raw, \"order\"),\n                      strings.icontains(.scan.ocr.raw, \"support\"),\n                      strings.icontains(.scan.ocr.raw, \"help line\"),\n                      strings.icontains(.scan.ocr.raw, \"receipt\"),\n                      strings.icontains(.scan.ocr.raw, \"invoice\"),\n                      strings.icontains(.scan.ocr.raw, \"call\"),\n                      strings.icontains(.scan.ocr.raw, \"helpdesk\"),\n                      strings.icontains(.scan.ocr.raw, \"cancel\"),\n                      strings.icontains(.scan.ocr.raw, \"renew\"),\n                      strings.icontains(.scan.ocr.raw, \"refund\"),\n                      strings.icontains(.scan.ocr.raw, \"amount\"),\n                      strings.icontains(.scan.ocr.raw, \"crypto\"),\n                      strings.icontains(.scan.ocr.raw, \"wallet address\"),\n                      regex.icontains(.scan.ocr.raw, '\\$\\d{3}\\.\\d{2}\\b'),\n                      regex.icontains(.scan.ocr.raw,\n                                      '(\\+\\d|1.(\\()?\\d{3}(\\))?\\D\\d{3}\\D\\d{4})'\n                      ),\n                      regex.icontains(.scan.ocr.raw,\n                                      '\\+?(\\d{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}\\d{3}[\\s\\.\\-⋅]{0,5}\\d{4}'\n                      )\n                    )\n\n                    // 1 of the following strings is found, representing common Callback brands\n                    and (\n                      1 of (\n                        strings.icontains(.scan.ocr.raw, \"geek squad\"),\n                        strings.icontains(.scan.ocr.raw, \"lifelock\"),\n                        strings.icontains(.scan.ocr.raw, \"best buy\"),\n                        strings.icontains(.scan.ocr.raw, \"mcafee\"),\n                        strings.icontains(.scan.ocr.raw, \"norton\"),\n                        strings.icontains(.scan.ocr.raw, \"ebay\"),\n                        strings.icontains(.scan.ocr.raw, \"paypal\"),\n                      )\n                      // add additional logic for common language for paypal, which is a valid payment method\n                      and not regex.icontains(.scan.ocr.raw,\n                                              \"paypal[^\\n]+accepted\"\n                      )\n                      and not regex.icontains(.scan.ocr.raw,\n                                              \"pay(ment)?.{0,30}(via|by) paypal\"\n                      )\n                      and not regex.icontains(.scan.ocr.raw,\n                                              '\\d{2,4} norton'\n                      ) // an address, example: 1234 Norton Road\n                      and not regex.icontains(.scan.ocr.raw,\n                                              'sold on.{0,20}ebay'\n                      ) // \"cannot be sold on Amazon and EBay\", from a legit vendor\n                    )\n                )"
             macro "any(ml.logo_detect(.).brands,\n                       .name in (\"PayPal\", \"Norton\", \"GeekSquad\", \"Ebay\")\n                )"
          attachments.file_extension eq "pdf"
           macro "any(file.explode(.), .scan.exiftool.page_count < 3)"
           macro "any(file.explode(.), length(.scan.ocr.raw) > 60)"
      attachments length_compare "3"
       macro "all(attachments, .file_extension == \"pdf\")"
    and
      attachments length_compare "0"
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                        (\n                          \"mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock\"\n                        )\n    )"
       macro "3 of (\n      strings.ilike(body.current_thread.text, '*purchase*'),\n      strings.ilike(body.current_thread.text, '*payment*'),\n      strings.ilike(body.current_thread.text, '*transaction*'),\n      strings.ilike(body.current_thread.text, '*subscription*'),\n      strings.ilike(body.current_thread.text, '*antivirus*'),\n      strings.ilike(body.current_thread.text, '*order*'),\n      strings.ilike(body.current_thread.text, '*support*'),\n      strings.ilike(body.current_thread.text, '*help line*'),\n      strings.ilike(body.current_thread.text, '*receipt*'),\n      strings.ilike(body.current_thread.text, '*invoice*'),\n      strings.ilike(body.current_thread.text, '*call*'),\n      strings.ilike(body.current_thread.text, '*cancel*'),\n      strings.ilike(body.current_thread.text, '*renew*'),\n      strings.ilike(body.current_thread.text, '*refund*')\n    )"
       macro "any([body.current_thread.text, subject.subject],\n            regex.icontains(., '\\b\\+?(\\d{1}.)?\\(?\\d{3}?\\)?.\\d{3}.?\\d{4}\\b')\n    )"
    and
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                          '\\+1'\n      )"
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                      '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+8\\d{9}.*\\n'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                       '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+1\\s?[ilo0-9]{3} [ilo0-9]{3} [ilo0-9]{4}.*\\n'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                       '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\\n'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                       '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+[ilo0-9]{3}\\.[ilo0-9]{3}\\.[ilo0-9]{4}.*\\n'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                       '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+\\([ilo0-9]{3}\\)-[ilo0-9]{3}-[ilo0-9]{4}.*\\n'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                       '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+\\([ilo0-9]{3}\\)[\\s-]+[ilo0-9]{3}[\\s-]+[ilo0-9]{4}.*\\n'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                       '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+\\+\\d{1,3}[ilo0-9]{10}.*\\n'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.html.inner_text),\n                    '(?:Sold|Bill)[\\s\\xa0]To(?:\\:\\s+|\\n)[^\\n]+\\+?(\\d{1}.)?\\(?\\d{3}?\\)?.\\d{3}.?\\d{4}.*\\n'\n    )"
  or
    not
      beta.profile.by_reply_to func_call "beta.profile.by_reply_to().any_messages_benign"
    headers.reply_to length_compare "0"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  sender.email.domain.root_domain in ["intuit.co.uk", "intuit.com"]
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Callback phishing via Microsoft comment

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

Detects callback scam messages originating from legitimate Microsoft infrastructure but containing fraudulent content designed to trick recipients into calling scammer phone numbers. The message includes typical callback phishing language around purchases, payments, subscriptions, or support services along with embedded phone numbers, while passing Microsoft's authentication checks.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesImpersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and length(attachments) == 0
and length(body.current_thread.text) < 1750

// Legitimate MicrosoftOnline sending infrastructure
// or invites@microsoft.com abuse
and (
  (
    sender.email.domain.root_domain in ('microsoftonline.com')
    or sender.email.email == "invites@microsoft.com"
  )

  // Callback Phishing
  and (
    any(ml.nlu_classifier(body.current_thread.text).intents,
        .name in ("callback_scam", "cred_theft")
        and .confidence in ("medium", "high")
    )
    or 3 of (
      strings.ilike(body.current_thread.text, '*purchase*'),
      strings.ilike(body.current_thread.text, '*payment*'),
      strings.ilike(body.current_thread.text, '*transaction*'),
      strings.ilike(body.current_thread.text, '*subscription*'),
      strings.ilike(body.current_thread.text, '*antivirus*'),
      strings.ilike(body.current_thread.text, '*order*'),
      strings.ilike(body.current_thread.text, '*support*'),
      strings.ilike(body.current_thread.text, '*help line*'),
      strings.ilike(body.current_thread.text, '*receipt*'),
      strings.ilike(body.current_thread.text, '*invoice*'),
      strings.ilike(body.current_thread.text, '*call*'),
      strings.ilike(body.current_thread.text, '*cancel*'),
      strings.ilike(body.current_thread.text, '*renew*'),
      strings.ilike(body.current_thread.text, '*refund*')
    )
    or regex.count(body.current_thread.text, '[^\x00-\x7F]') > 20
  )
  // phone number regex
  and any([body.current_thread.text, subject.subject],
          regex.icontains(.,
                          '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                          '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}',
                          '\(?[ilo0-9]{3}\)?[^ilo0-9]{0,3}\(?[ilo0-9]{3}\)?.?[ilo0-9]{4}'
          )
  )
)

Detection Logic

Scope: inbound message.

Detects callback scam messages originating from legitimate Microsoft infrastructure but containing fraudulent content designed to trick recipients into calling scammer phone numbers. The message includes typical callback phishing language around purchases, payments, subscriptions, or support services along with embedded phone numbers, while passing Microsoft's authentication checks.

  1. inbound message
  2. length(attachments) == 0
  3. length(body.current_thread.text) < 1750
  4. all of:
    • any of:
      • sender.email.domain.root_domain in ('microsoftonline.com')
      • sender.email.email is 'invites@microsoft.com'
    • any of:
      • any(ml.nlu_classifier(body.current_thread.text).intents, .name in ("callback_scam", "cred_theft") and .confidence in ("medium", "high") )
      • at least 3 of 14: body.current_thread.text matches any of 14 patterns
        • *purchase*
        • *payment*
        • *transaction*
        • *subscription*
        • *antivirus*
        • *order*
        • *support*
        • *help line*
        • *receipt*
        • *invoice*
        • *call*
        • *cancel*
        • *renew*
        • *refund*
      • regex.count(body.current_thread.text, '[^\x00-\x7F]') > 20
    • any([body.current_thread.text, subject.subject], regex.icontains(., '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}', '\(?[ilo0-9]{3}\)?[^ilo0-9]{0,3}\(?[ilo0-9]{3}\)?.?[ilo0-9]{4}' ) )

Inspects: body.current_thread.text, sender.email.domain.root_domain, sender.email.email, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.count, regex.icontains, strings.ilike.

Indicators Matched (24)

FieldMatchValue
sender.email.domain.root_domainmembermicrosoftonline.com
sender.email.emailequalsinvites@microsoft.com
ml.nlu_classifier(body.current_thread.text).intents[].namemembercallback_scam
ml.nlu_classifier(body.current_thread.text).intents[].namemembercred_theft
ml.nlu_classifier(body.current_thread.text).intents[].confidencemembermedium
ml.nlu_classifier(body.current_thread.text).intents[].confidencememberhigh
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
12 more
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
strings.ilikesubstring*invoice*
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
regex.countregex[^\x00-\x7F]
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
regex.icontainsregex\(?[ilo0-9]{3}\)?[^ilo0-9]{0,3}\(?[ilo0-9]{3}\)?.?[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
    regex.count func_call "regex.count(body.current_thread.text, '[^\\x00-\\x7F]') > 20"
     macro "3 of (\n      strings.ilike(body.current_thread.text, '*purchase*'),\n      strings.ilike(body.current_thread.text, '*payment*'),\n      strings.ilike(body.current_thread.text, '*transaction*'),\n      strings.ilike(body.current_thread.text, '*subscription*'),\n      strings.ilike(body.current_thread.text, '*antivirus*'),\n      strings.ilike(body.current_thread.text, '*order*'),\n      strings.ilike(body.current_thread.text, '*support*'),\n      strings.ilike(body.current_thread.text, '*help line*'),\n      strings.ilike(body.current_thread.text, '*receipt*'),\n      strings.ilike(body.current_thread.text, '*invoice*'),\n      strings.ilike(body.current_thread.text, '*call*'),\n      strings.ilike(body.current_thread.text, '*cancel*'),\n      strings.ilike(body.current_thread.text, '*renew*'),\n      strings.ilike(body.current_thread.text, '*refund*')\n    )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n        .name in (\"callback_scam\", \"cred_theft\")\n        and .confidence in (\"medium\", \"high\")\n    )"
  or
    sender.email.domain.root_domain eq "microsoftonline.com"
    sender.email.email eq "invites@microsoft.com"
  attachments length_compare "0"
  body.current_thread.text length_compare "1750"
  type.inbound eq "true"
   macro "any([body.current_thread.text, subject.subject],\n          regex.icontains(.,\n                          '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                          '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}',\n                          '\\(?[ilo0-9]{3}\\)?[^ilo0-9]{0,3}\\(?[ilo0-9]{3}\\)?.?[ilo0-9]{4}'\n          )\n  )"

Indicators

These rows show field, operator, and value matches.

Callback Phishing via Signable E-Signature Request

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

This rule inspects messages originating from legitimate Signable infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesExploit, Impersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) == 0
and (
  not beta.profile.by_reply_to().solicited
  or (
    beta.profile.by_reply_to().any_messages_malicious_or_spam
    and not beta.profile.by_reply_to().any_messages_benign
  )
)
// Legitimate Signable sending infratructure
and sender.email.domain.root_domain == 'signable.app'
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
and (
  // this section is synced with attachment_callback_phish_with_pdf.yml and attachment_callback_phish_with_img.yml
  regex.icontains(strings.replace_confusables(body.current_thread.text),
                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in (
           "PayPal",
           "Norton",
           "GeekSquad",
           "Ebay",
           "McAfee",
           "AT&T",
           "Microsoft"
         )
  )
)
and length(body.current_thread.text) < 1750
and (
  (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // however, the 3 of logic and requiring a phone number is specific to this rule in order to reduce FPs
    // caused by messages which mention cancelling or otherwise managing a subscription
    // it is also synced and below for message_screenshot OCR output
    3 of (
      strings.icontains(body.current_thread.text, 'purchase'),
      strings.icontains(body.current_thread.text, 'payment'),
      strings.icontains(body.current_thread.text, 'transaction'),
      strings.icontains(body.current_thread.text, 'subscription'),
      strings.icontains(body.current_thread.text, 'antivirus'),
      strings.icontains(body.current_thread.text, 'order'),
      strings.icontains(body.current_thread.text, 'support'),
      strings.icontains(body.current_thread.text, 'help line'),
      strings.icontains(body.current_thread.text, 'receipt'),
      strings.icontains(body.current_thread.text, 'invoice'),
      strings.icontains(body.current_thread.text, 'call'),
      strings.icontains(body.current_thread.text, 'cancel'),
      strings.icontains(body.current_thread.text, 'renew'),
      strings.icontains(body.current_thread.text, 'refund'),
      regex.icontains(body.current_thread.text, "(?:reach|contact) us at"),
      strings.icontains(body.current_thread.text, "+1"),
      strings.icontains(body.current_thread.text, "amount"),
      strings.icontains(body.current_thread.text, "charged"),
      strings.icontains(body.current_thread.text, "crypto"),
      strings.icontains(body.current_thread.text, "wallet address"),
      regex.icontains(body.current_thread.text, '\$\d{3}\.\d{2}\b'),
    )
    // phone number regex
    and regex.icontains(body.current_thread.text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )
  )
  or (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // and above for current_thread.text
    //
    // 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
    //
    3 of (
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'subscription'
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      "(?:reach|contact) us at"
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'wallet address'
      ),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      '\$\d{3}\.\d{2}\b'
      ),
    )
    // phone number regex
    and regex.icontains(beta.ocr(file.message_screenshot()).text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )

    // negate messages with previous threads.  While callback phishing with thread hijacking or with current_thread 
    // padded with whitespace and previous threads in the message has been observed, the intetion of using OCR is for image embedded callbacks
    and not regex.icount(beta.ocr(file.message_screenshot()).text,
                         '(?:from|to|sent|date|cc|subject):'
    ) > 3
    // this notation of previous threads often only occurs once
    and not regex.icontains(beta.ocr(file.message_screenshot()).text,
                            'wrote:[\r\n]'
    )
  )
)

Detection Logic

Scope: inbound message.

This rule inspects messages originating from legitimate Signable infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

  1. inbound message
  2. length(attachments) == 0
  3. any of:
    • not:
      • beta.profile.by_reply_to().solicited
    • all of:
      • beta.profile.by_reply_to().any_messages_malicious_or_spam
      • not:
        • beta.profile.by_reply_to().any_messages_benign
  4. sender.email.domain.root_domain is 'signable.app'
  5. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  6. any of:
    • regex.icontains(strings.replace_confusables(body.current_thread.text), '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)' )
    • any(ml.logo_detect(file.message_screenshot()).brands, .name in ( "PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T", "Microsoft" ) )
  7. length(body.current_thread.text) < 1750
  8. any of:
    • all of:
      • at least 3 of:
        • body.current_thread.text contains 'purchase'
        • body.current_thread.text contains 'payment'
        • body.current_thread.text contains 'transaction'
        • body.current_thread.text contains 'subscription'
        • body.current_thread.text contains 'antivirus'
        • body.current_thread.text contains 'order'
        • body.current_thread.text contains 'support'
        • body.current_thread.text contains 'help line'
        • body.current_thread.text contains 'receipt'
        • body.current_thread.text contains 'invoice'
        • body.current_thread.text contains 'call'
        • body.current_thread.text contains 'cancel'
        • body.current_thread.text contains 'renew'
        • body.current_thread.text contains 'refund'
        • body.current_thread.text matches '(?:reach|contact) us at'
        • body.current_thread.text contains '+1'
        • body.current_thread.text contains 'amount'
        • body.current_thread.text contains 'charged'
        • body.current_thread.text contains 'crypto'
        • body.current_thread.text contains 'wallet address'
        • body.current_thread.text matches '\\$\\d{3}\\.\\d{2}\\b'
      • body.current_thread.text matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
    • all of:
      • at least 3 of:
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'subscription' )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'order')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'support')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'call')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund')
        • regex.icontains(beta.ocr(file.message_screenshot()).text, "(?:reach|contact) us at" )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, '+1')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'wallet address' )
        • regex.icontains(beta.ocr(file.message_screenshot()).text, '\$\d{3}\.\d{2}\b' )
      • regex.icontains(beta.ocr(file.message_screenshot()).text, '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' )
      • not:
        • regex.icount(beta.ocr(file.message_screenshot()).text, '(?:from|to|sent|date|cc|subject):' ) > 3
      • not:
        • regex.icontains(beta.ocr(file.message_screenshot()).text, 'wrote:[\r\n]' )

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.email.domain.root_domain, type.inbound. Sensors: beta.ocr, beta.profile.by_reply_to, file.message_screenshot, ml.logo_detect, regex.icontains, regex.icount, strings.icontains, strings.replace_confusables.

Indicators Matched (33)

FieldMatchValue
sender.email.domain.root_domainequalssignable.app
regex.icontainsregex(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
ml.logo_detect(file.message_screenshot()).brands[].namememberMcAfee
ml.logo_detect(file.message_screenshot()).brands[].namememberAT&T
ml.logo_detect(file.message_screenshot()).brands[].namememberMicrosoft
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
21 more
strings.icontainssubstringsubscription
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
regex.icontainsregex(?:reach|contact) us at
strings.icontainssubstring+1
strings.icontainssubstringamount
strings.icontainssubstringcharged
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringhelpdesk

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        beta.profile.by_reply_to func_call "beta.profile.by_reply_to().any_messages_benign"
      beta.profile.by_reply_to func_call "beta.profile.by_reply_to().any_messages_malicious_or_spam"
    not
      beta.profile.by_reply_to func_call "beta.profile.by_reply_to().solicited"
  or
    and
      or
        body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
        body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
       macro "3 of (\n      strings.icontains(body.current_thread.text, 'purchase'),\n      strings.icontains(body.current_thread.text, 'payment'),\n      strings.icontains(body.current_thread.text, 'transaction'),\n      strings.icontains(body.current_thread.text, 'subscription'),\n      strings.icontains(body.current_thread.text, 'antivirus'),\n      strings.icontains(body.current_thread.text, 'order'),\n      strings.icontains(body.current_thread.text, 'support'),\n      strings.icontains(body.current_thread.text, 'help line'),\n      strings.icontains(body.current_thread.text, 'receipt'),\n      strings.icontains(body.current_thread.text, 'invoice'),\n      strings.icontains(body.current_thread.text, 'call'),\n      strings.icontains(body.current_thread.text, 'cancel'),\n      strings.icontains(body.current_thread.text, 'renew'),\n      strings.icontains(body.current_thread.text, 'refund'),\n      regex.icontains(body.current_thread.text, \"(?:reach|contact) us at\"),\n      strings.icontains(body.current_thread.text, \"+1\"),\n      strings.icontains(body.current_thread.text, \"amount\"),\n      strings.icontains(body.current_thread.text, \"charged\"),\n      strings.icontains(body.current_thread.text, \"crypto\"),\n      strings.icontains(body.current_thread.text, \"wallet address\"),\n      regex.icontains(body.current_thread.text, '\\$\\d{3}\\.\\d{2}\\b'),\n    )"
    and
      not
        regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                            'wrote:[\\r\\n]'\n    )"
      not
        regex.icount func_call "regex.icount(beta.ocr(file.message_screenshot()).text,\n                         '(?:from|to|sent|date|cc|subject):'\n    ) > 3"
      regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n    )"
       macro "3 of (\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'subscription'\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      \"(?:reach|contact) us at\"\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'wallet address'\n      ),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      '\\$\\d{3}\\.\\d{2}\\b'\n      ),\n    )"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  or
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'\n  )"
     macro "any(ml.logo_detect(file.message_screenshot()).brands,\n         .name in (\n           \"PayPal\",\n           \"Norton\",\n           \"GeekSquad\",\n           \"Ebay\",\n           \"McAfee\",\n           \"AT&T\",\n           \"Microsoft\"\n         )\n  )"
  attachments length_compare "0"
  body.current_thread.text length_compare "1750"
  sender.email.domain.root_domain eq "signable.app"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
sender.email.domain.root_domaineq
  • signable.app
field:"sender.email.domain.root_domain" kind:eq value:"signable.app"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via SignFree e-signature request

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

This rule inspects messages originating from legitimate SignFree infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesExploit, Impersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) == 0
and (
  not beta.profile.by_reply_to().solicited
  or (
    beta.profile.by_reply_to().any_messages_malicious_or_spam
    and not beta.profile.by_reply_to().any_messages_benign
  )
)
// Legitimate SignFree sending infratructure
and sender.email.domain.root_domain == 'signfree.io'
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
and (
  // this section is synced with attachment_callback_phish_with_pdf.yml and attachment_callback_phish_with_img.yml
  regex.icontains(strings.replace_confusables(body.current_thread.text),
                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in (
           "PayPal",
           "Norton",
           "GeekSquad",
           "Ebay",
           "McAfee",
           "AT&T",
           "Microsoft"
         )
  )
)
and (
  (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // however, the 3 of logic and requiring a phone number is specific to this rule in order to reduce FPs
    // caused by messages which mention cancelling or otherwise managing a subscription
    // it is also synced and below for message_screenshot OCR output
    3 of (
      strings.icontains(body.current_thread.text, 'purchase'),
      strings.icontains(body.current_thread.text, 'payment'),
      strings.icontains(body.current_thread.text, 'transaction'),
      strings.icontains(body.current_thread.text, 'subscription'),
      strings.icontains(body.current_thread.text, 'antivirus'),
      strings.icontains(body.current_thread.text, 'order'),
      strings.icontains(body.current_thread.text, 'support'),
      strings.icontains(body.current_thread.text, 'help line'),
      strings.icontains(body.current_thread.text, 'receipt'),
      strings.icontains(body.current_thread.text, 'invoice'),
      strings.icontains(body.current_thread.text, 'call'),
      strings.icontains(body.current_thread.text, 'cancel'),
      strings.icontains(body.current_thread.text, 'renew'),
      strings.icontains(body.current_thread.text, 'refund'),
      regex.icontains(body.current_thread.text, "(?:reach|contact) us at"),
      strings.icontains(body.current_thread.text, "+1"),
      strings.icontains(body.current_thread.text, "amount"),
      strings.icontains(body.current_thread.text, "charged"),
      strings.icontains(body.current_thread.text, "crypto"),
      strings.icontains(body.current_thread.text, "wallet address"),
      regex.icontains(body.current_thread.text, '\$\d{3}\.\d{2}\b'),
    )
    // phone number regex
    and regex.icontains(body.current_thread.text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )
  )
  or (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // and above for current_thread.text
    //
    // 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
    //
    3 of (
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'subscription'
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      "(?:reach|contact) us at"
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'wallet address'
      ),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      '\$\d{3}\.\d{2}\b'
      ),
    )
    // phone number regex
    and regex.icontains(beta.ocr(file.message_screenshot()).text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )

    // negate messages with previous threads.  While callback phishing with thread hijacking or with current_thread 
    // padded with whitespace and previous threads in the message has been observed, the intetion of using OCR is for image embedded callbacks
    and not regex.icount(beta.ocr(file.message_screenshot()).text,
                         '(?:from|to|sent|date|cc|subject):'
    ) > 3
    // this notation of previous threads often only occurs once
    and not regex.icontains(beta.ocr(file.message_screenshot()).text,
                            'wrote:[\r\n]'
    )
  )
)

Detection Logic

Scope: inbound message.

This rule inspects messages originating from legitimate SignFree infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

  1. inbound message
  2. length(attachments) == 0
  3. any of:
    • not:
      • beta.profile.by_reply_to().solicited
    • all of:
      • beta.profile.by_reply_to().any_messages_malicious_or_spam
      • not:
        • beta.profile.by_reply_to().any_messages_benign
  4. sender.email.domain.root_domain is 'signfree.io'
  5. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  6. any of:
    • regex.icontains(strings.replace_confusables(body.current_thread.text), '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)' )
    • any(ml.logo_detect(file.message_screenshot()).brands, .name in ( "PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T", "Microsoft" ) )
  7. any of:
    • all of:
      • at least 3 of:
        • body.current_thread.text contains 'purchase'
        • body.current_thread.text contains 'payment'
        • body.current_thread.text contains 'transaction'
        • body.current_thread.text contains 'subscription'
        • body.current_thread.text contains 'antivirus'
        • body.current_thread.text contains 'order'
        • body.current_thread.text contains 'support'
        • body.current_thread.text contains 'help line'
        • body.current_thread.text contains 'receipt'
        • body.current_thread.text contains 'invoice'
        • body.current_thread.text contains 'call'
        • body.current_thread.text contains 'cancel'
        • body.current_thread.text contains 'renew'
        • body.current_thread.text contains 'refund'
        • body.current_thread.text matches '(?:reach|contact) us at'
        • body.current_thread.text contains '+1'
        • body.current_thread.text contains 'amount'
        • body.current_thread.text contains 'charged'
        • body.current_thread.text contains 'crypto'
        • body.current_thread.text contains 'wallet address'
        • body.current_thread.text matches '\\$\\d{3}\\.\\d{2}\\b'
      • body.current_thread.text matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
    • all of:
      • at least 3 of:
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'subscription' )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'order')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'support')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'call')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund')
        • regex.icontains(beta.ocr(file.message_screenshot()).text, "(?:reach|contact) us at" )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, '+1')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'wallet address' )
        • regex.icontains(beta.ocr(file.message_screenshot()).text, '\$\d{3}\.\d{2}\b' )
      • regex.icontains(beta.ocr(file.message_screenshot()).text, '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' )
      • not:
        • regex.icount(beta.ocr(file.message_screenshot()).text, '(?:from|to|sent|date|cc|subject):' ) > 3
      • not:
        • regex.icontains(beta.ocr(file.message_screenshot()).text, 'wrote:[\r\n]' )

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.email.domain.root_domain, type.inbound. Sensors: beta.ocr, beta.profile.by_reply_to, file.message_screenshot, ml.logo_detect, regex.icontains, regex.icount, strings.icontains, strings.replace_confusables.

Indicators Matched (33)

FieldMatchValue
sender.email.domain.root_domainequalssignfree.io
regex.icontainsregex(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
ml.logo_detect(file.message_screenshot()).brands[].namememberMcAfee
ml.logo_detect(file.message_screenshot()).brands[].namememberAT&T
ml.logo_detect(file.message_screenshot()).brands[].namememberMicrosoft
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
21 more
strings.icontainssubstringsubscription
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
regex.icontainsregex(?:reach|contact) us at
strings.icontainssubstring+1
strings.icontainssubstringamount
strings.icontainssubstringcharged
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringhelpdesk

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        beta.profile.by_reply_to func_call "beta.profile.by_reply_to().any_messages_benign"
      beta.profile.by_reply_to func_call "beta.profile.by_reply_to().any_messages_malicious_or_spam"
    not
      beta.profile.by_reply_to func_call "beta.profile.by_reply_to().solicited"
  or
    and
      or
        body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
        body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
       macro "3 of (\n      strings.icontains(body.current_thread.text, 'purchase'),\n      strings.icontains(body.current_thread.text, 'payment'),\n      strings.icontains(body.current_thread.text, 'transaction'),\n      strings.icontains(body.current_thread.text, 'subscription'),\n      strings.icontains(body.current_thread.text, 'antivirus'),\n      strings.icontains(body.current_thread.text, 'order'),\n      strings.icontains(body.current_thread.text, 'support'),\n      strings.icontains(body.current_thread.text, 'help line'),\n      strings.icontains(body.current_thread.text, 'receipt'),\n      strings.icontains(body.current_thread.text, 'invoice'),\n      strings.icontains(body.current_thread.text, 'call'),\n      strings.icontains(body.current_thread.text, 'cancel'),\n      strings.icontains(body.current_thread.text, 'renew'),\n      strings.icontains(body.current_thread.text, 'refund'),\n      regex.icontains(body.current_thread.text, \"(?:reach|contact) us at\"),\n      strings.icontains(body.current_thread.text, \"+1\"),\n      strings.icontains(body.current_thread.text, \"amount\"),\n      strings.icontains(body.current_thread.text, \"charged\"),\n      strings.icontains(body.current_thread.text, \"crypto\"),\n      strings.icontains(body.current_thread.text, \"wallet address\"),\n      regex.icontains(body.current_thread.text, '\\$\\d{3}\\.\\d{2}\\b'),\n    )"
    and
      not
        regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                            'wrote:[\\r\\n]'\n    )"
      not
        regex.icount func_call "regex.icount(beta.ocr(file.message_screenshot()).text,\n                         '(?:from|to|sent|date|cc|subject):'\n    ) > 3"
      regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n    )"
       macro "3 of (\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'subscription'\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      \"(?:reach|contact) us at\"\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'wallet address'\n      ),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      '\\$\\d{3}\\.\\d{2}\\b'\n      ),\n    )"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  or
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'\n  )"
     macro "any(ml.logo_detect(file.message_screenshot()).brands,\n         .name in (\n           \"PayPal\",\n           \"Norton\",\n           \"GeekSquad\",\n           \"Ebay\",\n           \"McAfee\",\n           \"AT&T\",\n           \"Microsoft\"\n         )\n  )"
  attachments length_compare "0"
  sender.email.domain.root_domain eq "signfree.io"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
sender.email.domain.root_domaineq
  • signfree.io
field:"sender.email.domain.root_domain" kind:eq value:"signfree.io"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via Xodo Sign comment

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

This rule inspects messages originating from legitimate Xodo Sign infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesExploit, Impersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule Body

type.inbound
and length(attachments) == 0
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)
// Legitimate Xodo Sign/Eversign sending infratructure
and sender.email.domain.root_domain == 'eversign.com'
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
and (
  // this section is synced with attachment_callback_phish_with_pdf.yml and attachment_callback_phish_with_img.yml
  regex.icontains(strings.replace_confusables(body.current_thread.text),
                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T")
  )
)
and length(body.current_thread.text) < 1750
and (
  (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // however, the 3 of logic and requiring a phone number is specific to this rule in order to reduce FPs
    // caused by messages which mention cancelling or otherwise managing a subscription
    // it is also synced and below for message_screenshot OCR output
    3 of (
      strings.icontains(body.current_thread.text, 'purchase'),
      strings.icontains(body.current_thread.text, 'payment'),
      strings.icontains(body.current_thread.text, 'transaction'),
      strings.icontains(body.current_thread.text, 'subscription'),
      strings.icontains(body.current_thread.text, 'antivirus'),
      strings.icontains(body.current_thread.text, 'order'),
      strings.icontains(body.current_thread.text, 'support'),
      strings.icontains(body.current_thread.text, 'help line'),
      strings.icontains(body.current_thread.text, 'receipt'),
      strings.icontains(body.current_thread.text, 'invoice'),
      strings.icontains(body.current_thread.text, 'call'),
      strings.icontains(body.current_thread.text, 'cancel'),
      strings.icontains(body.current_thread.text, 'renew'),
      strings.icontains(body.current_thread.text, 'refund'),
      regex.icontains(body.current_thread.text, "(?:reach|contact) us at"),
      strings.icontains(body.current_thread.text, "+1"),
      strings.icontains(body.current_thread.text, "amount"),
      strings.icontains(body.current_thread.text, "charged"),
      strings.icontains(body.current_thread.text, "crypto"),
      strings.icontains(body.current_thread.text, "wallet address"),
      regex.icontains(body.current_thread.text, '\$\d{3}\.\d{2}\b'),
    )
    // phone number regex
    and regex.icontains(body.current_thread.text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )
  )
  or (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // and above for current_thread.text
    //
    // 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
    //
    3 of (
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'subscription'
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      "(?:reach|contact) us at"
      ),
      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),
      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),
      strings.icontains(beta.ocr(file.message_screenshot()).text,
                        'wallet address'
      ),
      regex.icontains(beta.ocr(file.message_screenshot()).text,
                      '\$\d{3}\.\d{2}\b'
      ),
    )
    // phone number regex
    and regex.icontains(beta.ocr(file.message_screenshot()).text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )

    // negate messages with previous threads.  While callback phishing with thread hijacking or with current_thread 
    // padded with whitespace and previous threads in the message has been observed, the intetion of using OCR is for image embedded callbacks
    and not regex.icount(beta.ocr(file.message_screenshot()).text,
                         '(?:from|to|sent|date|cc|subject):'
    ) > 3
    // this notation of previous threads often only occurs once
    and not regex.icontains(beta.ocr(file.message_screenshot()).text,
                            'wrote:[\r\n]'
    )
  )
)

Detection Logic

Scope: inbound message.

This rule inspects messages originating from legitimate Xodo Sign infrastructure, with content matching Callback Phishing criteria, in the body, requiring at least one brand name, as well as 3 matching Callback Phishing terms and a phone number.

  1. inbound message
  2. length(attachments) == 0
  3. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  4. sender.email.domain.root_domain is 'eversign.com'
  5. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  6. any of:
    • regex.icontains(strings.replace_confusables(body.current_thread.text), '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)' )
    • any(ml.logo_detect(file.message_screenshot()).brands, .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T") )
  7. length(body.current_thread.text) < 1750
  8. any of:
    • all of:
      • at least 3 of:
        • body.current_thread.text contains 'purchase'
        • body.current_thread.text contains 'payment'
        • body.current_thread.text contains 'transaction'
        • body.current_thread.text contains 'subscription'
        • body.current_thread.text contains 'antivirus'
        • body.current_thread.text contains 'order'
        • body.current_thread.text contains 'support'
        • body.current_thread.text contains 'help line'
        • body.current_thread.text contains 'receipt'
        • body.current_thread.text contains 'invoice'
        • body.current_thread.text contains 'call'
        • body.current_thread.text contains 'cancel'
        • body.current_thread.text contains 'renew'
        • body.current_thread.text contains 'refund'
        • body.current_thread.text matches '(?:reach|contact) us at'
        • body.current_thread.text contains '+1'
        • body.current_thread.text contains 'amount'
        • body.current_thread.text contains 'charged'
        • body.current_thread.text contains 'crypto'
        • body.current_thread.text contains 'wallet address'
        • body.current_thread.text matches '\\$\\d{3}\\.\\d{2}\\b'
      • body.current_thread.text matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
    • all of:
      • at least 3 of:
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'subscription' )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'order')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'support')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'call')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund')
        • regex.icontains(beta.ocr(file.message_screenshot()).text, "(?:reach|contact) us at" )
        • strings.icontains(beta.ocr(file.message_screenshot()).text, '+1')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto')
        • strings.icontains(beta.ocr(file.message_screenshot()).text, 'wallet address' )
        • regex.icontains(beta.ocr(file.message_screenshot()).text, '\$\d{3}\.\d{2}\b' )
      • regex.icontains(beta.ocr(file.message_screenshot()).text, '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' )
      • not:
        • regex.icount(beta.ocr(file.message_screenshot()).text, '(?:from|to|sent|date|cc|subject):' ) > 3
      • not:
        • regex.icontains(beta.ocr(file.message_screenshot()).text, 'wrote:[\r\n]' )

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.email.domain.root_domain, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.logo_detect, profile.by_sender, regex.icontains, regex.icount, strings.icontains, strings.replace_confusables.

Indicators Matched (32)

FieldMatchValue
sender.email.domain.root_domainequalseversign.com
regex.icontainsregex(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
ml.logo_detect(file.message_screenshot()).brands[].namememberMcAfee
ml.logo_detect(file.message_screenshot()).brands[].namememberAT&T
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
strings.icontainssubstringsubscription
20 more
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
regex.icontainsregex(?:reach|contact) us at
strings.icontainssubstring+1
strings.icontainssubstringamount
strings.icontainssubstringcharged
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringhelpdesk

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
        body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
       macro "3 of (\n      strings.icontains(body.current_thread.text, 'purchase'),\n      strings.icontains(body.current_thread.text, 'payment'),\n      strings.icontains(body.current_thread.text, 'transaction'),\n      strings.icontains(body.current_thread.text, 'subscription'),\n      strings.icontains(body.current_thread.text, 'antivirus'),\n      strings.icontains(body.current_thread.text, 'order'),\n      strings.icontains(body.current_thread.text, 'support'),\n      strings.icontains(body.current_thread.text, 'help line'),\n      strings.icontains(body.current_thread.text, 'receipt'),\n      strings.icontains(body.current_thread.text, 'invoice'),\n      strings.icontains(body.current_thread.text, 'call'),\n      strings.icontains(body.current_thread.text, 'cancel'),\n      strings.icontains(body.current_thread.text, 'renew'),\n      strings.icontains(body.current_thread.text, 'refund'),\n      regex.icontains(body.current_thread.text, \"(?:reach|contact) us at\"),\n      strings.icontains(body.current_thread.text, \"+1\"),\n      strings.icontains(body.current_thread.text, \"amount\"),\n      strings.icontains(body.current_thread.text, \"charged\"),\n      strings.icontains(body.current_thread.text, \"crypto\"),\n      strings.icontains(body.current_thread.text, \"wallet address\"),\n      regex.icontains(body.current_thread.text, '\\$\\d{3}\\.\\d{2}\\b'),\n    )"
    and
      not
        regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                            'wrote:[\\r\\n]'\n    )"
      not
        regex.icount func_call "regex.icount(beta.ocr(file.message_screenshot()).text,\n                         '(?:from|to|sent|date|cc|subject):'\n    ) > 3"
      regex.icontains func_call "regex.icontains(beta.ocr(file.message_screenshot()).text,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n    )"
       macro "3 of (\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'purchase'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'payment'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'transaction'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'subscription'\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'antivirus'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'order'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'support'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'help line'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'receipt'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'invoice'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'call'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'helpdesk'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'cancel'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'renew'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'refund'),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      \"(?:reach|contact) us at\"\n      ),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, '+1'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'amount'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'charged'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text, 'crypto'),\n      strings.icontains(beta.ocr(file.message_screenshot()).text,\n                        'wallet address'\n      ),\n      regex.icontains(beta.ocr(file.message_screenshot()).text,\n                      '\\$\\d{3}\\.\\d{2}\\b'\n      ),\n    )"
  or
    and
      not
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    not
      profile.by_sender func_call "profile.by_sender().solicited"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  or
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'\n  )"
     macro "any(ml.logo_detect(file.message_screenshot()).brands,\n         .name in (\"PayPal\", \"Norton\", \"GeekSquad\", \"Ebay\", \"McAfee\", \"AT&T\")\n  )"
  attachments length_compare "0"
  body.current_thread.text length_compare "1750"
  sender.email.domain.root_domain eq "eversign.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
sender.email.domain.root_domaineq
  • eversign.com
field:"sender.email.domain.root_domain" kind:eq value:"eversign.com"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via Yammer comment

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

Detects callback scams sent through Yammer infrastructure containing suspicious payment-related keywords and phone numbers. The rule identifies messages with callback scam language patterns or multiple financial transaction terms combined with phone number patterns in the message body or subject line.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesImpersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • sender.email
  • subject
  • type

Rule Body

type.inbound
// message from Yammer sending infratructure
and sender.email.domain.root_domain == 'yammer.com'
and length(body.current_thread.text) < 2000

// Callback Phishing
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name in ("callback_scam") and .confidence in ("medium", "high")
  )
  or 3 of (
    strings.ilike(body.current_thread.text, '*purchase*'),
    strings.ilike(body.current_thread.text, '*payment*'),
    strings.ilike(body.current_thread.text, '*transaction*'),
    strings.ilike(body.current_thread.text, '*subscription*'),
    strings.ilike(body.current_thread.text, '*antivirus*'),
    strings.ilike(body.current_thread.text, '*order*'),
    strings.ilike(body.current_thread.text, '*support*'),
    strings.ilike(body.current_thread.text, '*help line*'),
    strings.ilike(body.current_thread.text, '*receipt*'),
    strings.ilike(body.current_thread.text, '*invoice*'),
    strings.ilike(body.current_thread.text, '*call*'),
    strings.ilike(body.current_thread.text, '*cancel*'),
    strings.ilike(body.current_thread.text, '*renew*'),
    strings.ilike(body.current_thread.text, '*refund*')
  )
)
// phone number regex
and any([body.current_thread.text, subject.subject],
        regex.icontains(.,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
        )
)
// negate benign threads
and not any(ml.nlu_classifier(body.current_thread.text).intents,
            .name == "benign" and .confidence == "high"
)

Detection Logic

Scope: inbound message.

Detects callback scams sent through Yammer infrastructure containing suspicious payment-related keywords and phone numbers. The rule identifies messages with callback scam language patterns or multiple financial transaction terms combined with phone number patterns in the message body or subject line.

  1. inbound message
  2. sender.email.domain.root_domain is 'yammer.com'
  3. length(body.current_thread.text) < 2000
  4. any of:
    • any(ml.nlu_classifier(body.current_thread.text).intents, .name in ("callback_scam") and .confidence in ("medium", "high") )
    • at least 3 of 14: body.current_thread.text matches any of 14 patterns
      • *purchase*
      • *payment*
      • *transaction*
      • *subscription*
      • *antivirus*
      • *order*
      • *support*
      • *help line*
      • *receipt*
      • *invoice*
      • *call*
      • *cancel*
      • *renew*
      • *refund*
  5. any([body.current_thread.text, subject.subject], regex.icontains(., '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' ) )
  6. not:
    • any(ml.nlu_classifier(body.current_thread.text).intents, .name == "benign" and .confidence == "high" )

Inspects: body.current_thread.text, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.ilike.

Indicators Matched (20)

FieldMatchValue
sender.email.domain.root_domainequalsyammer.com
ml.nlu_classifier(body.current_thread.text).intents[].namemembercallback_scam
ml.nlu_classifier(body.current_thread.text).intents[].confidencemembermedium
ml.nlu_classifier(body.current_thread.text).intents[].confidencememberhigh
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
8 more
strings.ilikesubstring*receipt*
strings.ilikesubstring*invoice*
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
     macro "3 of (\n    strings.ilike(body.current_thread.text, '*purchase*'),\n    strings.ilike(body.current_thread.text, '*payment*'),\n    strings.ilike(body.current_thread.text, '*transaction*'),\n    strings.ilike(body.current_thread.text, '*subscription*'),\n    strings.ilike(body.current_thread.text, '*antivirus*'),\n    strings.ilike(body.current_thread.text, '*order*'),\n    strings.ilike(body.current_thread.text, '*support*'),\n    strings.ilike(body.current_thread.text, '*help line*'),\n    strings.ilike(body.current_thread.text, '*receipt*'),\n    strings.ilike(body.current_thread.text, '*invoice*'),\n    strings.ilike(body.current_thread.text, '*call*'),\n    strings.ilike(body.current_thread.text, '*cancel*'),\n    strings.ilike(body.current_thread.text, '*renew*'),\n    strings.ilike(body.current_thread.text, '*refund*')\n  )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n      .name in (\"callback_scam\") and .confidence in (\"medium\", \"high\")\n  )"
  not
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n            .name == \"benign\" and .confidence == \"high\"\n)"
  body.current_thread.text length_compare "2000"
  sender.email.domain.root_domain eq "yammer.com"
  type.inbound eq "true"
   macro "any([body.current_thread.text, subject.subject],\n        regex.icontains(.,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n        )\n)"

Indicators

These rows show field, operator, and value matches.

Callback phishing via Zelle Service Abuse

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

Callback phishing campaigns have been observed abusing Zelle services to send fraudulent payment requests with callback phishing contents.

Threat Classification

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

CategoryValues
Attack typesBEC/Fraud, Callback Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • body.html
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and length(attachments) == 0
and sender.email.domain.root_domain in ("zellepay.com")
and (
  // only seeing payment requests abused
  strings.ilike(body.html.display_text, "* requested*")
  // phone number in subject
  // the subject contains the seller's "name", attacks have been seen with the entire callback text in the seller's name
  or (
    regex.icontains(strings.replace_confusables(subject.subject),
                    '.*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*'
    )
    or regex.icontains(strings.replace_confusables(subject.subject),
                       '.*\+[ilo0-9]{1,3}[ilo0-9]{10}.*'
    )
    // +12028001238
    or regex.icontains(strings.replace_confusables(subject.subject),
                       '.*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*'
    )
    // 202-800-1238
    or regex.icontains(strings.replace_confusables(subject.subject),
                       '.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*'
    )
    // (202) 800-1238
    or regex.icontains(strings.replace_confusables(subject.subject),
                       '.*\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*'
    )
    // (202)-800-1238
    or regex.icontains(strings.replace_confusables(subject.subject),
                       '.*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*'
    )
    or ( // 8123456789
      regex.icontains(strings.replace_confusables(subject.subject),
                      '.*8[ilo0-9]{9}.*'
      )
      and regex.icontains(strings.replace_confusables(subject.subject),
                          '\+[1li]'
      )
    )
  )
)
and (
  (
    // icontains a phone number within the memo section (wrapped in quotes)
    (
      regex.icontains(strings.replace_confusables(body.current_thread.text),
                      '\".*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\"'
      )
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '\".*\+[ilo0-9]{1,3}[ilo0-9]{10}.*\"'
      )
      // +12028001238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '\".*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\"'
      )
      // 202-800-1238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '\".*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\"'
      )
      // (202) 800-1238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '\".*\([ilo0-9]{3}\)\s[ilo0-9]{3}-[ilo0-9]{4}.*\"'
      )
      // (202)-800-1238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '\".*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\"'
      )
      or ( // 8123456789
        regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '\".*8[ilo0-9]{9}.*\"'
        )
        and regex.icontains(strings.replace_confusables(body.current_thread.text
                            ),
                            '\".*\+[1li].*\"'
        )
      )
    )
    and (
      (
        4 of (
          strings.ilike(body.html.inner_text, '*"*you did not*"*'),
          strings.ilike(body.html.inner_text, '*"*is not for*"*'),
          strings.ilike(body.html.inner_text, '*"*done by you*"*'),
          regex.icontains(body.html.inner_text, "\".*didn\'t ma[kd]e this.*\""),
          strings.ilike(body.html.inner_text, '*"*Fruad Alert*"*'),
          strings.ilike(body.html.inner_text, '*"*Fraud Alert*"*'),
          strings.ilike(body.html.inner_text, '*"*fraudulent*"*'),
          strings.ilike(body.html.inner_text, '*"*Zelle*"*'),
          strings.ilike(body.html.inner_text, '*"*subscription*"*'),
          strings.ilike(body.html.inner_text, '*"*antivirus*"*'),
          strings.ilike(body.html.inner_text, '*"*order*"*'),
          strings.ilike(body.html.inner_text, '*"*support*"*'),
          strings.ilike(body.html.inner_text, '*"*sincerely apologize*"*'),
          strings.ilike(body.html.inner_text, '*"*receipt*"*'),
          strings.ilike(body.html.inner_text, '*"*invoice*"*'),
          strings.ilike(body.html.inner_text, '*"*Purchase*"*'),
          strings.ilike(body.html.inner_text, '*"*transaction*"*'),
          strings.ilike(body.html.inner_text, '*"*Market*Value*"*'),
          strings.ilike(body.html.inner_text, '*"*BTC*"*'),
          strings.ilike(body.html.inner_text, '*"*call*"*'),
          strings.ilike(body.html.inner_text, '*"*get in touch with our*"*'),
          strings.ilike(body.html.inner_text, '*"*quickly inform*"*'),
          strings.ilike(body.html.inner_text, '*"*quickly reach*"*'),
          strings.ilike(body.html.inner_text,
                        '*"*detected unusual transactions*'
          ),
          strings.ilike(body.html.inner_text,
                        '*"*without your authorization*"*'
          ),
          strings.ilike(body.html.inner_text, '*"*cancel*"*'),
          strings.ilike(body.html.inner_text, '*"*renew*"*'),
          strings.ilike(body.html.inner_text, '*"*refund*"*'),
          strings.ilike(body.html.inner_text, '*"*+1*"*'),
          regex.icontains(body.html.inner_text, '\"help.{0,3}desk'),
          strings.ilike(body.html.inner_text, '*"* your funds*"*'),
          strings.ilike(body.html.inner_text, '*"* your checking*"*'),
          strings.ilike(body.html.inner_text, '*"* your saving*"*'),
          strings.ilike(body.html.inner_text, '*"*transfer*"*'),
          strings.ilike(body.html.inner_text, '*"*secure your account*"*'),
          strings.ilike(body.html.inner_text, '*"*recover your *"*'),
        )
      )
      or regex.icontains(body.current_thread.text,
                         'note from.{0,50}(?:call|reach|contact|paypal)'
      )
      or any(ml.nlu_classifier(body.current_thread.text).intents,
             .name == "callback_scam"
      )
      or (
        // Unicode confusables words obfuscated in note
        regex.icontains(body.html.inner_text,
                        '\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹'
        )
      )
      or strings.ilike(body.html.inner_text, '*"*kindly*"*')
    )
  )
)

Detection Logic

Scope: inbound message.

Callback phishing campaigns have been observed abusing Zelle services to send fraudulent payment requests with callback phishing contents.

  1. inbound message
  2. length(attachments) == 0
  3. sender.email.domain.root_domain in ('zellepay.com')
  4. any of:
    • body.html.display_text matches '* requested*'
    • any of:
      • regex.icontains(strings.replace_confusables(subject.subject), '.*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*' )
      • regex.icontains(strings.replace_confusables(subject.subject), '.*\+[ilo0-9]{1,3}[ilo0-9]{10}.*' )
      • regex.icontains(strings.replace_confusables(subject.subject), '.*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*' )
      • regex.icontains(strings.replace_confusables(subject.subject), '.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*' )
      • regex.icontains(strings.replace_confusables(subject.subject), '.*\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*' )
      • regex.icontains(strings.replace_confusables(subject.subject), '.*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*' )
      • all of:
        • regex.icontains(strings.replace_confusables(subject.subject), '.*8[ilo0-9]{9}.*' )
        • regex.icontains(strings.replace_confusables(subject.subject), '\+[1li]' )
  5. all of:
    • any of:
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\"' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*\+[ilo0-9]{1,3}[ilo0-9]{10}.*\"' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\"' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\"' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*\([ilo0-9]{3}\)\s[ilo0-9]{3}-[ilo0-9]{4}.*\"' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\"' )
      • all of:
        • regex.icontains(strings.replace_confusables(body.current_thread.text), '\".*8[ilo0-9]{9}.*\"' )
        • regex.icontains(strings.replace_confusables(body.current_thread.text ), '\".*\+[1li].*\"' )
    • any of:
      • at least 4 of 36: body.html.inner_text matches any of 36 patterns
        • *"*you did not*"*
        • *"*is not for*"*
        • *"*done by you*"*
        • \".*didn\'t ma[kd]e this.*\"
        • *"*Fruad Alert*"*
        • *"*Fraud Alert*"*
        • *"*fraudulent*"*
        • *"*Zelle*"*
        • *"*subscription*"*
        • *"*antivirus*"*
        • *"*order*"*
        • *"*support*"*
        • *"*sincerely apologize*"*
        • *"*receipt*"*
        • *"*invoice*"*
        • *"*Purchase*"*
        • *"*transaction*"*
        • *"*Market*Value*"*
        • *"*BTC*"*
        • *"*call*"*
        • *"*get in touch with our*"*
        • *"*quickly inform*"*
        • *"*quickly reach*"*
        • *"*detected unusual transactions*
        • *"*without your authorization*"*
        • *"*cancel*"*
        • *"*renew*"*
        • *"*refund*"*
        • *"*+1*"*
        • \"help.{0,3}desk
        • *"* your funds*"*
        • *"* your checking*"*
        • *"* your saving*"*
        • *"*transfer*"*
        • *"*secure your account*"*
        • *"*recover your *"*
      • body.current_thread.text matches 'note from.{0,50}(?:call|reach|contact|paypal)'
      • any(ml.nlu_classifier(body.current_thread.text).intents, .name == "callback_scam" )
      • body.html.inner_text matches '\\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹'
      • body.html.inner_text matches '*"*kindly*"*'

Inspects: body.current_thread.text, body.html.display_text, body.html.inner_text, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.ilike, strings.replace_confusables.

Indicators Matched (58)

FieldMatchValue
sender.email.domain.root_domainmemberzellepay.com
strings.ilikesubstring* requested*
regex.icontainsregex.*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*
regex.icontainsregex.*\+[ilo0-9]{1,3}[ilo0-9]{10}.*
regex.icontainsregex.*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*
regex.icontainsregex.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*
regex.icontainsregex.*\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*
regex.icontainsregex.*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*
regex.icontainsregex.*8[ilo0-9]{9}.*
regex.icontainsregex\+[1li]
regex.icontainsregex\".*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\"
regex.icontainsregex\".*\+[ilo0-9]{1,3}[ilo0-9]{10}.*\"
46 more
regex.icontainsregex\".*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\"
regex.icontainsregex\".*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\"
regex.icontainsregex\".*\([ilo0-9]{3}\)\s[ilo0-9]{3}-[ilo0-9]{4}.*\"
regex.icontainsregex\".*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\"
regex.icontainsregex\".*8[ilo0-9]{9}.*\"
regex.icontainsregex\".*\+[1li].*\"
strings.ilikesubstring*"*you did not*"*
strings.ilikesubstring*"*is not for*"*
strings.ilikesubstring*"*done by you*"*
regex.icontainsregex\".*didn\'t ma[kd]e this.*\"
strings.ilikesubstring*"*Fruad Alert*"*
strings.ilikesubstring*"*Fraud Alert*"*
strings.ilikesubstring*"*fraudulent*"*
strings.ilikesubstring*"*Zelle*"*
strings.ilikesubstring*"*subscription*"*
strings.ilikesubstring*"*antivirus*"*
strings.ilikesubstring*"*order*"*
strings.ilikesubstring*"*support*"*
strings.ilikesubstring*"*sincerely apologize*"*
strings.ilikesubstring*"*receipt*"*
strings.ilikesubstring*"*invoice*"*
strings.ilikesubstring*"*Purchase*"*
strings.ilikesubstring*"*transaction*"*
strings.ilikesubstring*"*Market*Value*"*
strings.ilikesubstring*"*BTC*"*
strings.ilikesubstring*"*call*"*
strings.ilikesubstring*"*get in touch with our*"*
strings.ilikesubstring*"*quickly inform*"*
strings.ilikesubstring*"*quickly reach*"*
strings.ilikesubstring*"*detected unusual transactions*
strings.ilikesubstring*"*without your authorization*"*
strings.ilikesubstring*"*cancel*"*
strings.ilikesubstring*"*renew*"*
strings.ilikesubstring*"*refund*"*
strings.ilikesubstring*"*+1*"*
regex.icontainsregex\"help.{0,3}desk
strings.ilikesubstring*"* your funds*"*
strings.ilikesubstring*"* your checking*"*
strings.ilikesubstring*"* your saving*"*
strings.ilikesubstring*"*transfer*"*
strings.ilikesubstring*"*secure your account*"*
strings.ilikesubstring*"*recover your *"*
regex.icontainsregexnote from.{0,50}(?:call|reach|contact|paypal)
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscallback_scam
regex.icontainsregex\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹
strings.ilikesubstring*"*kindly*"*

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                        '\\\".*8[ilo0-9]{9}.*\\\"'\n        )"
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text\n                            ),\n                            '\\\".*\\+[1li].*\\\"'\n        )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '\\\".*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\\\"'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '\\\".*[ilo0-9]{3}\\.[ilo0-9]{3}\\.[ilo0-9]{4}.*\\\"'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '\\\".*\\([ilo0-9]{3}\\)-[ilo0-9]{3}-[ilo0-9]{4}.*\\\"'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '\\\".*\\([ilo0-9]{3}\\)\\s[ilo0-9]{3}-[ilo0-9]{4}.*\\\"'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '\\\".*\\+[ilo0-9]{1,3}[ilo0-9]{10}.*\\\"'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                      '\\\".*\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\\\"'\n      )"
  or
    and
      regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                          '\\+[1li]'\n      )"
      regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                      '.*8[ilo0-9]{9}.*'\n      )"
    body.html.display_text match " requested"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                       '.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                       '.*[ilo0-9]{3}\\.[ilo0-9]{3}\\.[ilo0-9]{4}.*'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                       '.*\\([ilo0-9]{3}\\)-[ilo0-9]{3}-[ilo0-9]{4}.*'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                       '.*\\([ilo0-9]{3}\\)[\\s-]+[ilo0-9]{3}[\\s-]+[ilo0-9]{4}.*'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                       '.*\\+[ilo0-9]{1,3}[ilo0-9]{10}.*'\n    )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(subject.subject),\n                    '.*\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*'\n    )"
  or
    body.current_thread.text regex_match "note from.{0,50}(?:call|reach|contact|paypal)"
    body.html.inner_text regex_match "\\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹"
    body.html.inner_text wildcard "*\"*kindly*\"*"
     macro "4 of (\n          strings.ilike(body.html.inner_text, '*\"*you did not*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*is not for*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*done by you*\"*'),\n          regex.icontains(body.html.inner_text, \"\\\".*didn\\'t ma[kd]e this.*\\\"\"),\n          strings.ilike(body.html.inner_text, '*\"*Fruad Alert*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*Fraud Alert*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*fraudulent*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*Zelle*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*subscription*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*antivirus*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*order*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*support*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*sincerely apologize*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*receipt*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*invoice*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*Purchase*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*transaction*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*Market*Value*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*BTC*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*call*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*get in touch with our*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*quickly inform*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*quickly reach*\"*'),\n          strings.ilike(body.html.inner_text,\n                        '*\"*detected unusual transactions*'\n          ),\n          strings.ilike(body.html.inner_text,\n                        '*\"*without your authorization*\"*'\n          ),\n          strings.ilike(body.html.inner_text, '*\"*cancel*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*renew*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*refund*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*+1*\"*'),\n          regex.icontains(body.html.inner_text, '\\\"help.{0,3}desk'),\n          strings.ilike(body.html.inner_text, '*\"* your funds*\"*'),\n          strings.ilike(body.html.inner_text, '*\"* your checking*\"*'),\n          strings.ilike(body.html.inner_text, '*\"* your saving*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*transfer*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*secure your account*\"*'),\n          strings.ilike(body.html.inner_text, '*\"*recover your *\"*'),\n        )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n             .name == \"callback_scam\"\n      )"
  attachments length_compare "0"
  sender.email.domain.root_domain eq "zellepay.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • note from.{0,50}(?:call|reach|contact|paypal) corpus 4 (mql 4)
field:"body.current_thread.text" kind:regex_match value:"note from.{0,50}(?:call|reach|contact|paypal)"
body.html.display_textwildcard
  • * requested*
field:"body.html.display_text" kind:wildcard value:"* requested*"
body.html.inner_textregex_match
    • +𝟭
    • 𝗽𝗮𝘆𝗺𝗲𝗻𝘁
    • 𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸
    • 𝗿𝗲𝗳𝘂𝗻𝗱
    • 𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀
    • 𝗰𝗮𝗹𝗹
    • 𝗰𝗮𝗻𝗰𝗲𝗹
    corpus 5 (mql 5)
field:"body.html.inner_text" kind:regex_match
body.html.inner_textwildcard
  • *"*kindly*"*
field:"body.html.inner_text" kind:wildcard
sender.email.domain.root_domainin
  • zellepay.com
field:"sender.email.domain.root_domain" kind:in value:"zellepay.com"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing via Zoho service abuse

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

Callback phishing campaigns have been observed abusing Zoho Invoice services to send fraudulent invoices with callback phishing contents.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesEvasion, Free email provider, Impersonation: Brand, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule Body

type.inbound

// Legitimate Zoho sending infratructure
and (
  sender.email.domain.root_domain in ('zohoinvoice.com')
  // check for SPF or DMARC passed
  and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)
)
and (
  // Callback Phishing in body (brand names required)
  (
    length(attachments) == 0

    // brand names are required.
    and regex.icontains(body.current_thread.text,
                        (
                          "mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock"
                        )
    )
    and 3 of (
      strings.ilike(body.current_thread.text, '*purchase*'),
      strings.ilike(body.current_thread.text, '*payment*'),
      strings.ilike(body.current_thread.text, '*transaction*'),
      strings.ilike(body.current_thread.text, '*subscription*'),
      strings.ilike(body.current_thread.text, '*antivirus*'),
      strings.ilike(body.current_thread.text, '*order*'),
      strings.ilike(body.current_thread.text, '*support*'),
      strings.ilike(body.current_thread.text, '*help line*'),
      strings.ilike(body.current_thread.text, '*receipt*'),
      strings.ilike(body.current_thread.text, '*invoice*'),
      strings.ilike(body.current_thread.text, '*call*'),
      strings.ilike(body.current_thread.text, '*cancel*'),
      strings.ilike(body.current_thread.text, '*renew*'),
      strings.ilike(body.current_thread.text, '*refund*')
    )
    // phone number regex
    and any([body.current_thread.text, subject.subject],
            regex.icontains(., '\b\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}\b')
    )
  )
  // all attachments are PDFs with callback phishing indicators Brands Required
  or (
    length(attachments) < 3
    and all(attachments, .file_extension == "pdf")
    // the attachment is a pdf with 1 page, and at least 60 ocr chars
    and any(attachments,
            (
              .file_extension == "pdf"
              and any(file.explode(.), .scan.exiftool.page_count < 3)
              and any(file.explode(.), length(.scan.ocr.raw) > 60)

              // 4 of the following strings are found        
              and (
                any(file.explode(.),
                    4 of (
                      strings.icontains(.scan.ocr.raw, "purchase"),
                      strings.icontains(.scan.ocr.raw, "payment"),
                      strings.icontains(.scan.ocr.raw, "transaction"),
                      strings.icontains(.scan.ocr.raw, "subscription"),
                      strings.icontains(.scan.ocr.raw, "antivirus"),
                      strings.icontains(.scan.ocr.raw, "order"),
                      strings.icontains(.scan.ocr.raw, "support"),
                      strings.icontains(.scan.ocr.raw, "help line"),
                      strings.icontains(.scan.ocr.raw, "receipt"),
                      strings.icontains(.scan.ocr.raw, "invoice"),
                      strings.icontains(.scan.ocr.raw, "call"),
                      strings.icontains(.scan.ocr.raw, "helpdesk"),
                      strings.icontains(.scan.ocr.raw, "cancel"),
                      strings.icontains(.scan.ocr.raw, "renew"),
                      strings.icontains(.scan.ocr.raw, "refund"),
                      strings.icontains(.scan.ocr.raw, "amount"),
                      strings.icontains(.scan.ocr.raw, "crypto"),
                      strings.icontains(.scan.ocr.raw, "wallet address"),
                      regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b'),
                      regex.icontains(.scan.ocr.raw,
                                      '(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})'
                      ),
                      regex.icontains(.scan.ocr.raw,
                                      '\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}'
                      )
                    )

                    // 1 of the following strings is found, representing common Callback brands          
                    and (
                      1 of (
                        strings.icontains(.scan.ocr.raw, "geek squad"),
                        strings.icontains(.scan.ocr.raw, "lifelock"),
                        strings.icontains(.scan.ocr.raw, "best buy"),
                        strings.icontains(.scan.ocr.raw, "mcafee"),
                        strings.icontains(.scan.ocr.raw, "norton"),
                        strings.icontains(.scan.ocr.raw, "ebay"),
                        strings.icontains(.scan.ocr.raw, "paypal"),
                      )
                      // add additional logic for common language for paypal, which is a valid payment method
                      and not regex.icontains(.scan.ocr.raw,
                                              "paypal[^\n]+accepted"
                      )
                      and not regex.icontains(.scan.ocr.raw,
                                              "payment.{0,30}(via|by) paypal"
                      )
                      and not regex.icontains(.scan.ocr.raw,
                                              '\d{2,4} norton'
                      ) // an address, example: 1234 Norton Road
                    )
                )
                or any(ml.logo_detect(.).brands,
                       .name in ("PayPal", "Norton", "GeekSquad", "Ebay")
                )
              )
            )
    )
  )
)

Detection Logic

Scope: inbound message.

Callback phishing campaigns have been observed abusing Zoho Invoice services to send fraudulent invoices with callback phishing contents.

  1. inbound message
  2. all of:
    • sender.email.domain.root_domain in ('zohoinvoice.com')
    • any of:
      • headers.auth_summary.spf.pass
      • headers.auth_summary.dmarc.pass
  3. any of:
    • all of:
      • length(attachments) == 0
      • body.current_thread.text matches 'mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock'
      • at least 3 of 14: body.current_thread.text matches any of 14 patterns
        • *purchase*
        • *payment*
        • *transaction*
        • *subscription*
        • *antivirus*
        • *order*
        • *support*
        • *help line*
        • *receipt*
        • *invoice*
        • *call*
        • *cancel*
        • *renew*
        • *refund*
      • any([body.current_thread.text, subject.subject], regex.icontains(., '\b\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}\b') )
    • all of:
      • length(attachments) < 3
      • all of attachments where:
        • .file_extension is 'pdf'
      • any of attachments where all hold:
        • .file_extension is 'pdf'
        • any(file.explode(.), .scan.exiftool.page_count < 3)
        • any(file.explode(.), length(.scan.ocr.raw) > 60)
        • any of:
          • any(file.explode(.), 4 of ( strings.icontains(.scan.ocr.raw, "purchase"), strings.icontains(.scan.ocr.raw, "payment"), strings.icontains(.scan.ocr.raw, "transaction"), strings.icontains(.scan.ocr.raw, "subscription"), strings.icontains(.scan.ocr.raw, "antivirus"), strings.icontains(.scan.ocr.raw, "order"), strings.icontains(.scan.ocr.raw, "support"), strings.icontains(.scan.ocr.raw, "help line"), strings.icontains(.scan.ocr.raw, "receipt"), strings.icontains(.scan.ocr.raw, "invoice"), strings.icontains(.scan.ocr.raw, "call"), strings.icontains(.scan.ocr.raw, "helpdesk"), strings.icontains(.scan.ocr.raw, "cancel"), strings.icontains(.scan.ocr.raw, "renew"), strings.icontains(.scan.ocr.raw, "refund"), strings.icontains(.scan.ocr.raw, "amount"), strings.icontains(.scan.ocr.raw, "crypto"), strings.icontains(.scan.ocr.raw, "wallet address"), regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b'), regex.icontains(.scan.ocr.raw, '(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})' ), regex.icontains(.scan.ocr.raw, '\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}' ) ) // 1 of the following strings is found, representing common Callback brands and ( 1 of ( strings.icontains(.scan.ocr.raw, "geek squad"), strings.icontains(.scan.ocr.raw, "lifelock"), strings.icontains(.scan.ocr.raw, "best buy"), strings.icontains(.scan.ocr.raw, "mcafee"), strings.icontains(.scan.ocr.raw, "norton"), strings.icontains(.scan.ocr.raw, "ebay"), strings.icontains(.scan.ocr.raw, "paypal"), ) // add additional logic for common language for paypal, which is a valid payment method and not regex.icontains(.scan.ocr.raw, "paypal[^\n]+accepted" ) and not regex.icontains(.scan.ocr.raw, "payment.{0,30}(via|by) paypal" ) and not regex.icontains(.scan.ocr.raw, '\d{2,4} norton' ) // an address, example: 1234 Norton Road ) )
          • any(ml.logo_detect(.).brands, .name in ("PayPal", "Norton", "GeekSquad", "Ebay") )

Inspects: attachments[].file_extension, body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: file.explode, ml.logo_detect, regex.icontains, strings.icontains, strings.ilike.

Indicators Matched (50)

FieldMatchValue
sender.email.domain.root_domainmemberzohoinvoice.com
regex.icontainsregexmcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
strings.ilikesubstring*invoice*
38 more
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
regex.icontainsregex\b\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}\b
attachments[].file_extensionequalspdf
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
strings.icontainssubstringsubscription
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringhelpdesk
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
strings.icontainssubstringamount
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})
regex.icontainsregex\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}
strings.icontainssubstringgeek squad
strings.icontainssubstringlifelock
strings.icontainssubstringbest buy
strings.icontainssubstringmcafee
strings.icontainssubstringnorton
strings.icontainssubstringebay
strings.icontainssubstringpaypal
ml.logo_detect(attachments[]).brands[].namememberPayPal
ml.logo_detect(attachments[]).brands[].namememberNorton
ml.logo_detect(attachments[]).brands[].namememberGeekSquad
ml.logo_detect(attachments[]).brands[].namememberEbay

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(attachments)
        and
          or
             macro "any(file.explode(.),\n                    4 of (\n                      strings.icontains(.scan.ocr.raw, \"purchase\"),\n                      strings.icontains(.scan.ocr.raw, \"payment\"),\n                      strings.icontains(.scan.ocr.raw, \"transaction\"),\n                      strings.icontains(.scan.ocr.raw, \"subscription\"),\n                      strings.icontains(.scan.ocr.raw, \"antivirus\"),\n                      strings.icontains(.scan.ocr.raw, \"order\"),\n                      strings.icontains(.scan.ocr.raw, \"support\"),\n                      strings.icontains(.scan.ocr.raw, \"help line\"),\n                      strings.icontains(.scan.ocr.raw, \"receipt\"),\n                      strings.icontains(.scan.ocr.raw, \"invoice\"),\n                      strings.icontains(.scan.ocr.raw, \"call\"),\n                      strings.icontains(.scan.ocr.raw, \"helpdesk\"),\n                      strings.icontains(.scan.ocr.raw, \"cancel\"),\n                      strings.icontains(.scan.ocr.raw, \"renew\"),\n                      strings.icontains(.scan.ocr.raw, \"refund\"),\n                      strings.icontains(.scan.ocr.raw, \"amount\"),\n                      strings.icontains(.scan.ocr.raw, \"crypto\"),\n                      strings.icontains(.scan.ocr.raw, \"wallet address\"),\n                      regex.icontains(.scan.ocr.raw, '\\$\\d{3}\\.\\d{2}\\b'),\n                      regex.icontains(.scan.ocr.raw,\n                                      '(\\+\\d|1.(\\()?\\d{3}(\\))?\\D\\d{3}\\D\\d{4})'\n                      ),\n                      regex.icontains(.scan.ocr.raw,\n                                      '\\+?(\\d{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}\\d{3}[\\s\\.\\-⋅]{0,5}\\d{4}'\n                      )\n                    )\n\n                    // 1 of the following strings is found, representing common Callback brands          \n                    and (\n                      1 of (\n                        strings.icontains(.scan.ocr.raw, \"geek squad\"),\n                        strings.icontains(.scan.ocr.raw, \"lifelock\"),\n                        strings.icontains(.scan.ocr.raw, \"best buy\"),\n                        strings.icontains(.scan.ocr.raw, \"mcafee\"),\n                        strings.icontains(.scan.ocr.raw, \"norton\"),\n                        strings.icontains(.scan.ocr.raw, \"ebay\"),\n                        strings.icontains(.scan.ocr.raw, \"paypal\"),\n                      )\n                      // add additional logic for common language for paypal, which is a valid payment method\n                      and not regex.icontains(.scan.ocr.raw,\n                                              \"paypal[^\\n]+accepted\"\n                      )\n                      and not regex.icontains(.scan.ocr.raw,\n                                              \"payment.{0,30}(via|by) paypal\"\n                      )\n                      and not regex.icontains(.scan.ocr.raw,\n                                              '\\d{2,4} norton'\n                      ) // an address, example: 1234 Norton Road\n                    )\n                )"
             macro "any(ml.logo_detect(.).brands,\n                       .name in (\"PayPal\", \"Norton\", \"GeekSquad\", \"Ebay\")\n                )"
          attachments.file_extension eq "pdf"
           macro "any(file.explode(.), .scan.exiftool.page_count < 3)"
           macro "any(file.explode(.), length(.scan.ocr.raw) > 60)"
      attachments length_compare "3"
       macro "all(attachments, .file_extension == \"pdf\")"
    and
      attachments length_compare "0"
      body.current_thread.text regex_match "mcafee|norton|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifelock"
       macro "3 of (\n      strings.ilike(body.current_thread.text, '*purchase*'),\n      strings.ilike(body.current_thread.text, '*payment*'),\n      strings.ilike(body.current_thread.text, '*transaction*'),\n      strings.ilike(body.current_thread.text, '*subscription*'),\n      strings.ilike(body.current_thread.text, '*antivirus*'),\n      strings.ilike(body.current_thread.text, '*order*'),\n      strings.ilike(body.current_thread.text, '*support*'),\n      strings.ilike(body.current_thread.text, '*help line*'),\n      strings.ilike(body.current_thread.text, '*receipt*'),\n      strings.ilike(body.current_thread.text, '*invoice*'),\n      strings.ilike(body.current_thread.text, '*call*'),\n      strings.ilike(body.current_thread.text, '*cancel*'),\n      strings.ilike(body.current_thread.text, '*renew*'),\n      strings.ilike(body.current_thread.text, '*refund*')\n    )"
       macro "any([body.current_thread.text, subject.subject],\n            regex.icontains(., '\\b\\+?(\\d{1}.)?\\(?\\d{3}?\\)?.\\d{3}.?\\d{4}\\b')\n    )"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  sender.email.domain.root_domain eq "zohoinvoice.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Callback Phishing via Zoom comment

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

Detects callback scams sent through legitimate Zoom infrastructure that impersonate well-known brands like McAfee, Norton, or PayPal. These messages contain purchase or support-related language along with phone numbers, attempting to trick recipients into calling fraudulent support lines.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesOut of band pivot, Social engineering, Impersonation: Brand

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and length(attachments) == 0

// Legitimate Zoom sending infratructure
and sender.email.domain.root_domain == 'zoom.us'
and (headers.auth_summary.spf.pass or headers.auth_summary.dmarc.pass)

// Zoom Logo
and any(ml.logo_detect(file.message_screenshot()).brands, .name == "Zoom")

// Callback Phishing
and regex.icontains(body.current_thread.text,
                    (
                      "mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck"
                    )
)
and (
  3 of (
    strings.ilike(body.current_thread.text, '*purchase*'),
    strings.ilike(body.current_thread.text, '*payment*'),
    strings.ilike(body.current_thread.text, '*transaction*'),
    strings.ilike(body.current_thread.text, '*subscription*'),
    strings.ilike(body.current_thread.text, '*antivirus*'),
    strings.ilike(body.current_thread.text, '*order*'),
    strings.ilike(body.current_thread.text, '*support*'),
    strings.ilike(body.current_thread.text, '*help line*'),
    strings.ilike(body.current_thread.text, '*receipt*'),
    strings.ilike(body.current_thread.text, '*invoice*'),
    strings.ilike(body.current_thread.text, '*call*'),
    strings.ilike(body.current_thread.text, '*cancel*'),
    strings.ilike(body.current_thread.text, '*renew*'),
    strings.ilike(body.current_thread.text, '*refund*'),
    strings.ilike(body.current_thread.text, '*host key*')
  )
  or any(ml.nlu_classifier(body.current_thread.text).intents,
         .name == "callback_scam" and .confidence != "low"
  )
)
// phone number regex
and any([body.current_thread.text, subject.subject],
        regex.icontains(.,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
        )
)
// negation for legitimate AI generated meeting summaries from Zoom
and not (
  (
    sender.display_name == "Meeting Summary with AI Companion"
    and sender.email.email == "no-reply@zoom.us"
    and headers.auth_summary.dmarc.pass
  )
  or (
    strings.icontains(subject.subject, "Meeting assets")
    and strings.icontains(body.current_thread.text, "Meeting summary")
    and sender.email.email == "no-reply@zoom.us"
    and headers.auth_summary.dmarc.pass
  )
)

Detection Logic

Scope: inbound message.

Detects callback scams sent through legitimate Zoom infrastructure that impersonate well-known brands like McAfee, Norton, or PayPal. These messages contain purchase or support-related language along with phone numbers, attempting to trick recipients into calling fraudulent support lines.

  1. inbound message
  2. length(attachments) == 0
  3. sender.email.domain.root_domain is 'zoom.us'
  4. any of:
    • headers.auth_summary.spf.pass
    • headers.auth_summary.dmarc.pass
  5. any(ml.logo_detect(file.message_screenshot()).brands, .name == "Zoom")
  6. body.current_thread.text matches 'mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck'
  7. any of:
    • at least 3 of 15: body.current_thread.text matches any of 15 patterns
      • *purchase*
      • *payment*
      • *transaction*
      • *subscription*
      • *antivirus*
      • *order*
      • *support*
      • *help line*
      • *receipt*
      • *invoice*
      • *call*
      • *cancel*
      • *renew*
      • *refund*
      • *host key*
    • any(ml.nlu_classifier(body.current_thread.text).intents, .name == "callback_scam" and .confidence != "low" )
  8. any([body.current_thread.text, subject.subject], regex.icontains(., '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}', '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}' ) )
  9. none of:
    • all of:
      • sender.display_name is 'Meeting Summary with AI Companion'
      • sender.email.email is 'no-reply@zoom.us'
      • headers.auth_summary.dmarc.pass
    • all of:
      • subject.subject contains 'Meeting assets'
      • body.current_thread.text contains 'Meeting summary'
      • sender.email.email is 'no-reply@zoom.us'
      • headers.auth_summary.dmarc.pass

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, sender.display_name, sender.email.domain.root_domain, sender.email.email, subject.subject, type.inbound. Sensors: file.message_screenshot, ml.logo_detect, ml.nlu_classifier, regex.icontains, strings.icontains, strings.ilike.

Indicators Matched (21)

FieldMatchValue
sender.email.domain.root_domainequalszoom.us
ml.logo_detect(file.message_screenshot()).brands[].nameequalsZoom
regex.icontainsregexmcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck
strings.ilikesubstring*purchase*
strings.ilikesubstring*payment*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
9 more
strings.ilikesubstring*invoice*
strings.ilikesubstring*call*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
strings.ilikesubstring*host key*
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscallback_scam
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  not
    or
      and
        body.current_thread.text contains "Meeting summary"
        headers.auth_summary.dmarc.pass eq "true"
        sender.email.email eq "no-reply@zoom.us"
        subject.subject contains "Meeting assets"
      and
        headers.auth_summary.dmarc.pass eq "true"
        sender.display_name eq "Meeting Summary with AI Companion"
        sender.email.email eq "no-reply@zoom.us"
  or
    headers.auth_summary.dmarc.pass eq "true"
    headers.auth_summary.spf.pass eq "true"
  or
     macro "3 of (\n    strings.ilike(body.current_thread.text, '*purchase*'),\n    strings.ilike(body.current_thread.text, '*payment*'),\n    strings.ilike(body.current_thread.text, '*transaction*'),\n    strings.ilike(body.current_thread.text, '*subscription*'),\n    strings.ilike(body.current_thread.text, '*antivirus*'),\n    strings.ilike(body.current_thread.text, '*order*'),\n    strings.ilike(body.current_thread.text, '*support*'),\n    strings.ilike(body.current_thread.text, '*help line*'),\n    strings.ilike(body.current_thread.text, '*receipt*'),\n    strings.ilike(body.current_thread.text, '*invoice*'),\n    strings.ilike(body.current_thread.text, '*call*'),\n    strings.ilike(body.current_thread.text, '*cancel*'),\n    strings.ilike(body.current_thread.text, '*renew*'),\n    strings.ilike(body.current_thread.text, '*refund*'),\n    strings.ilike(body.current_thread.text, '*host key*')\n  )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n         .name == \"callback_scam\" and .confidence != \"low\"\n  )"
  attachments length_compare "0"
  body.current_thread.text regex_match "mcafee|n[o0]rt[o0]n|geek.{0,5}squad|paypal|ebay|symantec|best buy|lifel[o0]ck"
  sender.email.domain.root_domain eq "zoom.us"
  type.inbound eq "true"
   macro "any([body.current_thread.text, subject.subject],\n        regex.icontains(.,\n                        '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}',\n                        '\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}'\n        )\n)"
   macro "any(ml.logo_detect(file.message_screenshot()).brands, .name == \"Zoom\")"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
    • mcafee
    • n[o0]rt[o0]n
    • geek.{0,5}squad
    • paypal
    • ebay
    • symantec
    • best buy
    • lifel[o0]ck
    corpus 7 (mql 7)
field:"body.current_thread.text" kind:regex_match
headers.auth_summary.dmarc.passeq
  • true transforms: boolean corpus 39 (mql 39)
field:"headers.auth_summary.dmarc.pass" kind:eq value:"true"
headers.auth_summary.spf.passeq
  • true transforms: boolean corpus 32 (mql 32)
field:"headers.auth_summary.spf.pass" kind:eq value:"true"
sender.email.domain.root_domaineq
  • zoom.us corpus 2 (mql 2)
field:"sender.email.domain.root_domain" kind:eq value:"zoom.us"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Exclusions

The rule actively suppresses these predicates.

Callback phishing: AOL senders with suspicious HTML template or PDF attachment

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

Detects a specific behavioral pattern from AOL senders using consistent HTML templates and PDF attachment characteristics. The pattern includes particular Yahoo-style formatting with specific font families, and standardized PDF metadata when attachments are present.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesFree email provider, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.html
  • headers
  • recipients
  • sender.email
  • type

Rule Body

type.inbound
// sender is aol
and sender.email.domain.root_domain == "aol.com"

// there is a single recipient
and length(recipients.to) == 1
and length(recipients.cc) == 0
and length(recipients.bcc) == 0

// is not a reply
and headers.in_reply_to is null

// this is another way to limit replies but need to account for 
// a behavior where Yahoo/Aol seems to include it's own message ID as a reference
and length(headers.references) == 1

// all message-id values end in mail.yahoo.com, some benign messages use @aol.com
and strings.iends_with(headers.message_id, '@mail.yahoo.com>')

// All are using the legit Yahoo X-Mailer
// this removes AOL sent from iphones and other non AOL client
and strings.ends_with(headers.mailer, 'AolMailNorrin')

// exclude common recipients which interact with aol addresses that are commonly (but not always) benign 
// recipeint exclusions are suggested for other recipients
and (
  // no attachment
  (
    length(attachments) == 0
    // if there are no attachments focus on the HTML template being observed
    and (

      // yahoo div followed by <br>
      // // verdana, helvetica, sans-serif;
      strings.icontains(body.html.raw,
                        "<div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n"
      )
      // // Helvetica Neue, Helvetica, Arial, sans-serif;
      or strings.icontains(body.html.raw,
                           "<div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n"
      )
      // yahoo div followed by doctype 
      // // verdana, helvetica, sans-serif;
      or strings.icontains(body.html.raw,
                           "<div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>"
      )
      // // Helvetica Neue, Helvetica, Arial, sans-serif;
      or strings.icontains(body.html.raw,
                           "<div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>"
      )
    )
  )
  or (
    // if there is an attachment, there can be only one
    length(attachments) == 1
    and all(attachments,
            // it's a PDF, 
            .file_type == "pdf"
            and (
              // static content_id value for the attachments
              (
                .content_id == '<@yahoo.com>' or .content_id == '@yahoo.com'
              )
              // created by observed static PDF details
              or (

                // 
                // 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
                // 
                beta.parse_exif(.).creator == "wkhtmltopdf 0.12.6"
                and beta.parse_exif(.).title is null
                and beta.parse_exif(.).producer == 'Qt 4.8.7'
              )
            )
    )
  )
)

Detection Logic

Scope: inbound message.

Detects a specific behavioral pattern from AOL senders using consistent HTML templates and PDF attachment characteristics. The pattern includes particular Yahoo-style formatting with specific font families, and standardized PDF metadata when attachments are present.

  1. inbound message
  2. sender.email.domain.root_domain is 'aol.com'
  3. length(recipients.to) == 1
  4. length(recipients.cc) == 0
  5. length(recipients.bcc) == 0
  6. headers.in_reply_to is missing
  7. length(headers.references) == 1
  8. headers.message_id ends with '@mail.yahoo.com>'
  9. headers.mailer ends with 'AolMailNorrin'
  10. any of:
    • all of:
      • length(attachments) == 0
      • body.html.raw contains any of 4 patterns
        • <div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n
        • <div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n
        • <div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>
        • <div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>
    • all of:
      • length(attachments) == 1
      • all of attachments where all hold:
        • .file_type is 'pdf'
        • any of:
          • any of:
            • .content_id is '<@yahoo.com>'
            • .content_id is '@yahoo.com'
          • all of:
            • beta.parse_exif(.).creator == "wkhtmltopdf 0.12.6"
            • beta.parse_exif(.).title is null
            • beta.parse_exif(.).producer == 'Qt 4.8.7'

Inspects: attachments[].content_id, attachments[].file_type, body.html.raw, headers.in_reply_to, headers.mailer, headers.message_id, headers.references, recipients.bcc, recipients.cc, recipients.to, sender.email.domain.root_domain, type.inbound. Sensors: beta.parse_exif, strings.ends_with, strings.icontains, strings.iends_with.

Indicators Matched (10)

FieldMatchValue
sender.email.domain.root_domainequalsaol.com
strings.iends_withsuffix@mail.yahoo.com>
strings.ends_withsuffixAolMailNorrin
strings.icontainssubstring<div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n
strings.icontainssubstring<div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n
strings.icontainssubstring<div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>
strings.icontainssubstring<div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>
attachments[].file_typeequalspdf
attachments[].content_idequals<@yahoo.com>
attachments[].content_idequals@yahoo.com

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        body.html.raw contains "<div class=\\\"yahoo-style-wrap\\\" style=\\\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\\\">\\r\\n<div dir=\\\"ltr\\\" data-setdir=\\\"false\\\"><!DOCTYPE html>"
        body.html.raw contains "<div class=\\\"yahoo-style-wrap\\\" style=\\\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\\\">\\r\\n<div dir=\\\"ltr\\\" data-setdir=\\\"false\\\"><br>\\r\\n"
        body.html.raw contains "<div class=\\\"yahoo-style-wrap\\\" style=\\\"font-family:verdana, helvetica, sans-serif;font-size:18px;\\\">\\r\\n<div dir=\\\"ltr\\\" data-setdir=\\\"false\\\"><!DOCTYPE html>"
        body.html.raw contains "<div class=\\\"yahoo-style-wrap\\\" style=\\\"font-family:verdana, helvetica, sans-serif;font-size:18px;\\\">\\r\\n<div dir=\\\"ltr\\\" data-setdir=\\\"false\\\"><br>\\r\\n"
      attachments length_compare "0"
    and
      attachments length_compare "1"
       macro "all(attachments,\n            // it's a PDF, \n            .file_type == \"pdf\"\n            and (\n              // static content_id value for the attachments\n              (\n                .content_id == '<@yahoo.com>' or .content_id == '@yahoo.com'\n              )\n              // created by observed static PDF details\n              or (\n\n                // \n                // This rule makes use of a beta feature and is subject to change without notice\n                // using the beta feature in custom rules is not suggested until it has been formally released\n                // \n                beta.parse_exif(.).creator == \"wkhtmltopdf 0.12.6\"\n                and beta.parse_exif(.).title is null\n                and beta.parse_exif(.).producer == 'Qt 4.8.7'\n              )\n            )\n    )"
  headers.in_reply_to is_null
  headers.mailer ends_with "AolMailNorrin"
  headers.message_id ends_with "@mail.yahoo.com>"
  headers.references length_compare "1"
  recipients.bcc length_compare "0"
  recipients.cc length_compare "0"
  recipients.to length_compare "1"
  sender.email.domain.root_domain eq "aol.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawcontains
  • <div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>
  • <div class=\"yahoo-style-wrap\" style=\"font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n
  • <div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><!DOCTYPE html>
  • <div class=\"yahoo-style-wrap\" style=\"font-family:verdana, helvetica, sans-serif;font-size:18px;\">\r\n<div dir=\"ltr\" data-setdir=\"false\"><br>\r\n
field:"body.html.raw" kind:contains
headers.in_reply_tois_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_null
headers.mailerends_with
  • AolMailNorrin
field:"headers.mailer" kind:ends_with value:"AolMailNorrin"
headers.message_idends_with
  • @mail.yahoo.com>
field:"headers.message_id" kind:ends_with value:"@mail.yahoo.com>"
sender.email.domain.root_domaineq
  • aol.com
field:"sender.email.domain.root_domain" kind:eq value:"aol.com"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing: Branded invoice from sender/reply-to domain less than 30 days old

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

This rule checks for invoicing content from a sender, reply-to domain or return-path domain less than 30d old. It also checks the body or the OCR'd screenshot for key words commonly abused in fraudulent invoicing attacks.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesImpersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • headers
  • headers.reply_to
  • headers.return_path
  • sender.email
  • type

Rule Body

type.inbound
// reply to domain that's less than 30d old and doesn't match the sender
and (
  (
    length(headers.reply_to) > 0
    and all(headers.reply_to,
            network.whois(.email.domain).days_old <= 30
            and .email.email != sender.email.email
    )
  )

  // or the return path or sender domain is less than 30d old 
  or network.whois(headers.return_path.domain).days_old <= 30
  or network.whois(sender.email.domain).days_old <= 30
)

// invoicing with high confidence
and any(ml.nlu_classifier(body.current_thread.text).tags,
        .name == "invoice" and .confidence == "high"
)

// commonly abused brands in body
and (
  strings.ilike(body.current_thread.text,
                "*mcafee*",
                "*norton*",
                "*geek squad*",
                "*paypal*",
                "*ebay*",
                "*symantec*",
                "*best buy*",
                "*lifelock*",
                "*virus*"
  )

  // commonly abused brand logo
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in ("PayPal", "Norton", "GeekSquad", "Ebay")
  )

  // check message screenshot ocr for commonly abused brands
  //
  // 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 1 of (
    strings.icontains(beta.ocr(file.message_screenshot()).text, "geek squad"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "lifelock"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "best buy"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "mcafee"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "norton"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "ebay"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "paypal"),
    strings.icontains(beta.ocr(file.message_screenshot()).text, "virus"),
  )
)

// phone number regex
and regex.icontains(body.current_thread.text,
                    '\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}'
)
and not profile.by_sender().solicited
and not profile.by_sender().any_messages_benign

Detection Logic

Scope: inbound message.

This rule checks for invoicing content from a sender, reply-to domain or return-path domain less than 30d old. It also checks the body or the OCR'd screenshot for key words commonly abused in fraudulent invoicing attacks.

  1. inbound message
  2. any of:
    • all of:
      • length(headers.reply_to) > 0
      • all of headers.reply_to where all hold:
        • network.whois(.email.domain).days_old <= 30
        • .email.email is not sender.email.email
    • network.whois(headers.return_path.domain).days_old <= 30
    • network.whois(sender.email.domain).days_old <= 30
  3. any(ml.nlu_classifier(body.current_thread.text).tags, .name == "invoice" and .confidence == "high" )
  4. any of:
    • body.current_thread.text matches any of 9 patterns
      • *mcafee*
      • *norton*
      • *geek squad*
      • *paypal*
      • *ebay*
      • *symantec*
      • *best buy*
      • *lifelock*
      • *virus*
    • any(ml.logo_detect(file.message_screenshot()).brands, .name in ("PayPal", "Norton", "GeekSquad", "Ebay") )
    • at least 1 of:
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "geek squad")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "lifelock")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "best buy")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "mcafee")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "norton")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "ebay")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "paypal")
      • strings.icontains(beta.ocr(file.message_screenshot()).text, "virus")
  5. body.current_thread.text matches '\\+?(\\d{1}.)?\\(?\\d{3}?\\)?.\\d{3}.?\\d{4}'
  6. not:
    • profile.by_sender().solicited
  7. not:
    • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, headers.reply_to, headers.reply_to[].email.domain, headers.reply_to[].email.email, headers.return_path.domain, sender.email.domain, sender.email.email, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.logo_detect, ml.nlu_classifier, network.whois, profile.by_sender, regex.icontains, strings.icontains, strings.ilike.

Indicators Matched (24)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).tags[].nameequalsinvoice
ml.nlu_classifier(body.current_thread.text).tags[].confidenceequalshigh
strings.ilikesubstring*mcafee*
strings.ilikesubstring*norton*
strings.ilikesubstring*geek squad*
strings.ilikesubstring*paypal*
strings.ilikesubstring*ebay*
strings.ilikesubstring*symantec*
strings.ilikesubstring*best buy*
strings.ilikesubstring*lifelock*
strings.ilikesubstring*virus*
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
12 more
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
strings.icontainssubstringgeek squad
strings.icontainssubstringlifelock
strings.icontainssubstringbest buy
strings.icontainssubstringmcafee
strings.icontainssubstringnorton
strings.icontainssubstringebay
strings.icontainssubstringpaypal
strings.icontainssubstringvirus
regex.icontainsregex\+?(\d{1}.)?\(?\d{3}?\)?.\d{3}.?\d{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      headers.reply_to length_compare "0"
       macro "all(headers.reply_to,\n            network.whois(.email.domain).days_old <= 30\n            and .email.email != sender.email.email\n    )"
    network.whois func_call "network.whois(headers.return_path.domain).days_old <= 30"
    network.whois func_call "network.whois(sender.email.domain).days_old <= 30"
  or
    body.current_thread.text match "best buy"
    body.current_thread.text match "ebay"
    body.current_thread.text match "geek squad"
    body.current_thread.text match "lifelock"
    body.current_thread.text match "mcafee"
    body.current_thread.text match "norton"
    body.current_thread.text match "paypal"
    body.current_thread.text match "symantec"
    body.current_thread.text match "virus"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"best buy\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"ebay\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"geek squad\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"lifelock\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"mcafee\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"norton\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"paypal\")"
    strings.icontains func_call "strings.icontains(beta.ocr(file.message_screenshot()).text, \"virus\")"
     macro "any(ml.logo_detect(file.message_screenshot()).brands,\n         .name in (\"PayPal\", \"Norton\", \"GeekSquad\", \"Ebay\")\n  )"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  body.current_thread.text regex_match "\\+?(\\d{1}.)?\\(?\\d{3}?\\)?.\\d{3}.?\\d{4}"
  type.inbound eq "true"
   macro "any(ml.nlu_classifier(body.current_thread.text).tags,\n        .name == \"invoice\" and .confidence == \"high\"\n)"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?(\d{1}.)?(?\d{3}?)?.\d{3}.?\d{4}
field:"body.current_thread.text" kind:regex_match value:"+?(\d{1}.)?(?\d{3}?)?.\d{3}.?\d{4}"
body.current_thread.textwildcard
  • *best buy* corpus 2 (mql 2)
  • *ebay* corpus 3 (mql 3)
  • *geek squad*
  • *lifelock* corpus 2 (mql 2)
  • *mcafee* corpus 3 (mql 3)
  • *norton* corpus 3 (mql 3)
  • *paypal* corpus 4 (mql 4)
  • *symantec* corpus 2 (mql 2)
  • *virus*
field:"body.current_thread.text" kind:wildcard
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing: Social Security Administration fraud

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

Detects phishing attempts that impersonate the Social Security Administration, using a single PDF attachment containing specific fraud-related language and a callback number.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesEvasion, Free email provider, Out of band pivot, PDF, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • sender.email
  • type

Rule Body

type.inbound
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

// single attachment
and length(attachments) == 1

// sender is freemail
and sender.email.domain.root_domain in $free_email_providers

// the attachment is a pdf with 1 page, and at least 60 ocr chars
and any(attachments,
        .file_extension == "pdf"
        and any(file.explode(.), .scan.exiftool.page_count == 1)
        and any(file.explode(.), length(.scan.ocr.raw) > 60)

        // 4 of the following strings are found        
        and (
          any(file.explode(.),
              4 of (
                strings.icontains(.scan.ocr.raw, "fraudulent activity"),
                strings.icontains(.scan.ocr.raw, "Social Security Number"),
                strings.icontains(.scan.ocr.raw, "SSN"),
                strings.icontains(.scan.ocr.raw, "stolen"),
                strings.icontains(.scan.ocr.raw, "illicit activities"),
                strings.icontains(.scan.ocr.raw,
                                  "Social Security Administration"
                ),
                strings.icontains(.scan.ocr.raw, "if you are innocent"),
                strings.icontains(.scan.ocr.raw, "help line"),
                strings.icontains(.scan.ocr.raw, "Department of Justice"),
                strings.icontains(.scan.ocr.raw, "innocent"),
                regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b')
                and (
                  regex.contains(.scan.ocr.raw,
                                 '(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})'
                  )
                  or regex.contains(.scan.ocr.raw,
                                    '\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}'
                  )
                )
              )
          )
        )
)

Detection Logic

Scope: inbound message.

Detects phishing attempts that impersonate the Social Security Administration, using a single PDF attachment containing specific fraud-related language and a callback number.

  1. inbound message
  2. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  3. length(attachments) == 1
  4. sender.email.domain.root_domain in $free_email_providers
  5. any of attachments where all hold:
    • .file_extension is 'pdf'
    • any(file.explode(.), .scan.exiftool.page_count == 1)
    • any(file.explode(.), length(.scan.ocr.raw) > 60)
    • ( any(file.explode(.), 4 of ( strings.icontains(.scan.ocr.raw, "fraudulent activity"), strings.icontains(.scan.ocr.raw, "Social Security Number"), strings.icontains(.scan.ocr.raw, "SSN"), strings.icontains(.scan.ocr.raw, "stolen"), strings.icontains(.scan.ocr.raw, "illicit activities"), strings.icontains(.scan.ocr.raw, "Social Security Administration" ), strings.icontains(.scan.ocr.raw, "if you are innocent"), strings.icontains(.scan.ocr.raw, "help line"), strings.icontains(.scan.ocr.raw, "Department of Justice"), strings.icontains(.scan.ocr.raw, "innocent"), regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b') and ( regex.contains(.scan.ocr.raw, '(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})' ) or regex.contains(.scan.ocr.raw, '\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}' ) ) ) ) )

Inspects: attachments[].file_extension, sender.email.domain.root_domain, type.inbound. Sensors: file.explode, profile.by_sender, regex.contains, regex.icontains, strings.icontains. Reference lists: $free_email_providers.

Indicators Matched (14)

FieldMatchValue
attachments[].file_extensionequalspdf
strings.icontainssubstringfraudulent activity
strings.icontainssubstringSocial Security Number
strings.icontainssubstringSSN
strings.icontainssubstringstolen
strings.icontainssubstringillicit activities
strings.icontainssubstringSocial Security Administration
strings.icontainssubstringif you are innocent
strings.icontainssubstringhelp line
strings.icontainssubstringDepartment of Justice
strings.icontainssubstringinnocent
regex.icontainsregex\$\d{3}\.\d{2}\b
2 more
regex.containsregex(\+\d|1.(\()?\d{3}(\))?\D\d{3}\D\d{4})
regex.containsregex\+?(\d{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}\d{3}[\s\.\-⋅]{0,5}\d{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    not
      profile.by_sender func_call "profile.by_sender().solicited"
  any(attachments)
    and
      attachments.file_extension eq "pdf"
       macro "(\n          any(file.explode(.),\n              4 of (\n                strings.icontains(.scan.ocr.raw, \"fraudulent activity\"),\n                strings.icontains(.scan.ocr.raw, \"Social Security Number\"),\n                strings.icontains(.scan.ocr.raw, \"SSN\"),\n                strings.icontains(.scan.ocr.raw, \"stolen\"),\n                strings.icontains(.scan.ocr.raw, \"illicit activities\"),\n                strings.icontains(.scan.ocr.raw,\n                                  \"Social Security Administration\"\n                ),\n                strings.icontains(.scan.ocr.raw, \"if you are innocent\"),\n                strings.icontains(.scan.ocr.raw, \"help line\"),\n                strings.icontains(.scan.ocr.raw, \"Department of Justice\"),\n                strings.icontains(.scan.ocr.raw, \"innocent\"),\n                regex.icontains(.scan.ocr.raw, '\\$\\d{3}\\.\\d{2}\\b')\n                and (\n                  regex.contains(.scan.ocr.raw,\n                                 '(\\+\\d|1.(\\()?\\d{3}(\\))?\\D\\d{3}\\D\\d{4})'\n                  )\n                  or regex.contains(.scan.ocr.raw,\n                                    '\\+?(\\d{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}\\d{3}[\\s\\.\\-⋅]{0,5}\\d{4}'\n                  )\n                )\n              )\n          )\n        )"
       macro "any(file.explode(.), .scan.exiftool.page_count == 1)"
       macro "any(file.explode(.), length(.scan.ocr.raw) > 60)"
  attachments length_compare "1"
  sender.email.domain.root_domain macro "sender.email.domain.root_domain in free_email_providers"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing: SumUp infrastructure abuse

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

A fraudulent invoice/receipt found in the body of the message sent by exploiting SumUp's receipt email service.

Threat Classification

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

CategoryValues
Attack typesBEC/Fraud, Callback Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • body.html
  • sender.email
  • type

Rule Body

type.inbound
and sender.email.domain.root_domain in ("sumup.com")
and (
  strings.ilike(body.html.display_text, "*delivery note*")
  or strings.ilike(body.html.display_text, "*made with sumup*")
)
// keep in sync with https://github.com/sublime-security/sublime-rules/blob/main/detection-rules/paypal_invoice_abuse.yml
and (
  (
    // icontains a phone number
    (
      regex.icontains(strings.replace_confusables(body.current_thread.text),
                      '.*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\n'
      )
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '.*\+[ilo0-9]{1,3}[ilo0-9]{10}.*\n'
      )
      // +12028001238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '.*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\n'
      )
      // 202-800-1238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\n'
      )
      // (202) 800-1238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '.*\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*\n'
      )
      // (202)-800-1238
      or regex.icontains(strings.replace_confusables(body.current_thread.text),
                         '.*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\n'
      )
      or ( // 8123456789
        regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '.*8[ilo0-9]{9}.*\n'
        )
        and regex.icontains(strings.replace_confusables(body.current_thread.text
                            ),
                            '\+[1l]'
        )
      )
    )
    and (
      (
        4 of (
          strings.ilike(body.html.inner_text, '*you did not*'),
          strings.ilike(body.html.inner_text, '*is not for*'),
          strings.ilike(body.html.inner_text, '*done by you*'),
          regex.icontains(body.html.inner_text, "didn\'t ma[kd]e this"),
          strings.ilike(body.html.inner_text, '*Fruad Alert*'),
          strings.ilike(body.html.inner_text, '*Fraud Alert*'),
          strings.ilike(body.html.inner_text, '*fraudulent*'),
          strings.ilike(body.html.inner_text, '*using your PayPal*'),
          strings.ilike(body.html.inner_text, '*subscription*'),
          strings.ilike(body.html.inner_text, '*antivirus*'),
          strings.ilike(body.html.inner_text, '*order*'),
          strings.ilike(body.html.inner_text, '*support*'),
          strings.ilike(body.html.inner_text, '*sincerely apologize*'),
          strings.ilike(body.html.inner_text, '*receipt*'),
          strings.ilike(body.html.inner_text, '*invoice*'),
          strings.ilike(body.html.inner_text, '*Purchase*'),
          strings.ilike(body.html.inner_text, '*transaction*'),
          strings.ilike(body.html.inner_text, '*Market*Value*'),
          strings.ilike(body.html.inner_text, '*BTC*'),
          strings.ilike(body.html.inner_text, '*call*'),
          strings.ilike(body.html.inner_text, '*get in touch with our*'),
          strings.ilike(body.html.inner_text, '*quickly inform*'),
          strings.ilike(body.html.inner_text, '*quickly reach *'),
          strings.ilike(body.html.inner_text, '*detected unusual transactions*'),
          strings.ilike(body.html.inner_text, '*without your authorization*'),
          strings.ilike(body.html.inner_text, '*cancel*'),
          strings.ilike(body.html.inner_text, '*renew*'),
          strings.ilike(body.html.inner_text, '*refund*'),
          strings.ilike(body.html.inner_text, '*+1*'),
          regex.icontains(body.html.inner_text, 'help.{0,3}desk'),
          strings.ilike(body.html.inner_text, '* your funds*'),
          strings.ilike(body.html.inner_text, '* your checking*'),
          strings.ilike(body.html.inner_text, '* your saving*'),
          strings.ilike(body.html.inner_text, '*transfer*'),
          strings.ilike(body.html.inner_text, '*secure your account*'),
          strings.ilike(body.html.inner_text, '*recover your*'),
          strings.ilike(body.html.inner_text, '*unusual activity*'),
          strings.ilike(body.html.inner_text, '*suspicious transaction*'),
          strings.ilike(body.html.inner_text, '*transaction history*'),
          strings.ilike(body.html.inner_text, '*please ignore this*'),
          strings.ilike(body.html.inner_text, '*report activity*'),
        )
      )
      or regex.icontains(body.current_thread.text,
                         'note from.{0,50}(?:call|reach|contact|paypal)'
      )
      or any(ml.nlu_classifier(body.current_thread.text).intents,
             .name == "callback_scam"
      )
      or (
        // Unicode confusables words obfuscated in note
        regex.icontains(body.html.inner_text,
                        '\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹'
        )
      )
      or strings.ilike(body.html.inner_text, '*kindly*')
    )
  )
)

Detection Logic

Scope: inbound message.

A fraudulent invoice/receipt found in the body of the message sent by exploiting SumUp's receipt email service.

  1. inbound message
  2. sender.email.domain.root_domain in ('sumup.com')
  3. any of:
    • body.html.display_text matches '*delivery note*'
    • body.html.display_text matches '*made with sumup*'
  4. all of:
    • any of:
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*\+[ilo0-9]{1,3}[ilo0-9]{10}.*\n' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*\n' )
      • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\n' )
      • all of:
        • regex.icontains(strings.replace_confusables(body.current_thread.text), '.*8[ilo0-9]{9}.*\n' )
        • regex.icontains(strings.replace_confusables(body.current_thread.text ), '\+[1l]' )
    • any of:
      • at least 4 of 41: body.html.inner_text matches any of 41 patterns
        • *you did not*
        • *is not for*
        • *done by you*
        • didn\'t ma[kd]e this
        • *Fruad Alert*
        • *Fraud Alert*
        • *fraudulent*
        • *using your PayPal*
        • *subscription*
        • *antivirus*
        • *order*
        • *support*
        • *sincerely apologize*
        • *receipt*
        • *invoice*
        • *Purchase*
        • *transaction*
        • *Market*Value*
        • *BTC*
        • *call*
        • *get in touch with our*
        • *quickly inform*
        • *quickly reach *
        • *detected unusual transactions*
        • *without your authorization*
        • *cancel*
        • *renew*
        • *refund*
        • *+1*
        • help.{0,3}desk
        • * your funds*
        • * your checking*
        • * your saving*
        • *transfer*
        • *secure your account*
        • *recover your*
        • *unusual activity*
        • *suspicious transaction*
        • *transaction history*
        • *please ignore this*
        • *report activity*
      • body.current_thread.text matches 'note from.{0,50}(?:call|reach|contact|paypal)'
      • any(ml.nlu_classifier(body.current_thread.text).intents, .name == "callback_scam" )
      • body.html.inner_text matches '\\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹'
      • body.html.inner_text matches '*kindly*'

Inspects: body.current_thread.text, body.html.display_text, body.html.inner_text, sender.email.domain.root_domain, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.ilike, strings.replace_confusables.

Indicators Matched (56)

FieldMatchValue
sender.email.domain.root_domainmembersumup.com
strings.ilikesubstring*delivery note*
strings.ilikesubstring*made with sumup*
regex.icontainsregex.*\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\n
regex.icontainsregex.*\+[ilo0-9]{1,3}[ilo0-9]{10}.*\n
regex.icontainsregex.*[ilo0-9]{3}\.[ilo0-9]{3}\.[ilo0-9]{4}.*\n
regex.icontainsregex.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\n
regex.icontainsregex.*\([ilo0-9]{3}\)[\s-]+[ilo0-9]{3}[\s-]+[ilo0-9]{4}.*\n
regex.icontainsregex.*\([ilo0-9]{3}\)-[ilo0-9]{3}-[ilo0-9]{4}.*\n
regex.icontainsregex.*8[ilo0-9]{9}.*\n
regex.icontainsregex\+[1l]
strings.ilikesubstring*you did not*
44 more
strings.ilikesubstring*is not for*
strings.ilikesubstring*done by you*
regex.icontainsregexdidn\'t ma[kd]e this
strings.ilikesubstring*Fruad Alert*
strings.ilikesubstring*Fraud Alert*
strings.ilikesubstring*fraudulent*
strings.ilikesubstring*using your PayPal*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
strings.ilikesubstring*sincerely apologize*
strings.ilikesubstring*receipt*
strings.ilikesubstring*invoice*
strings.ilikesubstring*Purchase*
strings.ilikesubstring*transaction*
strings.ilikesubstring*Market*Value*
strings.ilikesubstring*BTC*
strings.ilikesubstring*call*
strings.ilikesubstring*get in touch with our*
strings.ilikesubstring*quickly inform*
strings.ilikesubstring*quickly reach *
strings.ilikesubstring*detected unusual transactions*
strings.ilikesubstring*without your authorization*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
strings.ilikesubstring*+1*
regex.icontainsregexhelp.{0,3}desk
strings.ilikesubstring* your funds*
strings.ilikesubstring* your checking*
strings.ilikesubstring* your saving*
strings.ilikesubstring*transfer*
strings.ilikesubstring*secure your account*
strings.ilikesubstring*recover your*
strings.ilikesubstring*unusual activity*
strings.ilikesubstring*suspicious transaction*
strings.ilikesubstring*transaction history*
strings.ilikesubstring*please ignore this*
strings.ilikesubstring*report activity*
regex.icontainsregexnote from.{0,50}(?:call|reach|contact|paypal)
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscallback_scam
regex.icontainsregex\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹
strings.ilikesubstring*kindly*

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                        '.*8[ilo0-9]{9}.*\\n'\n        )"
      regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text\n                            ),\n                            '\\+[1l]'\n        )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '.*[ilo0-9]{3}-[ilo0-9]{3}-[ilo0-9]{4}.*\\n'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '.*[ilo0-9]{3}\\.[ilo0-9]{3}\\.[ilo0-9]{4}.*\\n'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '.*\\([ilo0-9]{3}\\)-[ilo0-9]{3}-[ilo0-9]{4}.*\\n'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '.*\\([ilo0-9]{3}\\)[\\s-]+[ilo0-9]{3}[\\s-]+[ilo0-9]{4}.*\\n'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                         '.*\\+[ilo0-9]{1,3}[ilo0-9]{10}.*\\n'\n      )"
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                      '.*\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}.*\\n'\n      )"
  or
    body.current_thread.text regex_match "note from.{0,50}(?:call|reach|contact|paypal)"
    body.html.inner_text match "kindly"
    body.html.inner_text regex_match "\\+𝟭|𝗽𝗮𝘆𝗺𝗲𝗻𝘁|𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸|𝗿𝗲𝗳𝘂𝗻𝗱|𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀|𝗰𝗮𝗹𝗹|𝗰𝗮𝗻𝗰𝗲𝗹"
     macro "4 of (\n          strings.ilike(body.html.inner_text, '*you did not*'),\n          strings.ilike(body.html.inner_text, '*is not for*'),\n          strings.ilike(body.html.inner_text, '*done by you*'),\n          regex.icontains(body.html.inner_text, \"didn\\'t ma[kd]e this\"),\n          strings.ilike(body.html.inner_text, '*Fruad Alert*'),\n          strings.ilike(body.html.inner_text, '*Fraud Alert*'),\n          strings.ilike(body.html.inner_text, '*fraudulent*'),\n          strings.ilike(body.html.inner_text, '*using your PayPal*'),\n          strings.ilike(body.html.inner_text, '*subscription*'),\n          strings.ilike(body.html.inner_text, '*antivirus*'),\n          strings.ilike(body.html.inner_text, '*order*'),\n          strings.ilike(body.html.inner_text, '*support*'),\n          strings.ilike(body.html.inner_text, '*sincerely apologize*'),\n          strings.ilike(body.html.inner_text, '*receipt*'),\n          strings.ilike(body.html.inner_text, '*invoice*'),\n          strings.ilike(body.html.inner_text, '*Purchase*'),\n          strings.ilike(body.html.inner_text, '*transaction*'),\n          strings.ilike(body.html.inner_text, '*Market*Value*'),\n          strings.ilike(body.html.inner_text, '*BTC*'),\n          strings.ilike(body.html.inner_text, '*call*'),\n          strings.ilike(body.html.inner_text, '*get in touch with our*'),\n          strings.ilike(body.html.inner_text, '*quickly inform*'),\n          strings.ilike(body.html.inner_text, '*quickly reach *'),\n          strings.ilike(body.html.inner_text, '*detected unusual transactions*'),\n          strings.ilike(body.html.inner_text, '*without your authorization*'),\n          strings.ilike(body.html.inner_text, '*cancel*'),\n          strings.ilike(body.html.inner_text, '*renew*'),\n          strings.ilike(body.html.inner_text, '*refund*'),\n          strings.ilike(body.html.inner_text, '*+1*'),\n          regex.icontains(body.html.inner_text, 'help.{0,3}desk'),\n          strings.ilike(body.html.inner_text, '* your funds*'),\n          strings.ilike(body.html.inner_text, '* your checking*'),\n          strings.ilike(body.html.inner_text, '* your saving*'),\n          strings.ilike(body.html.inner_text, '*transfer*'),\n          strings.ilike(body.html.inner_text, '*secure your account*'),\n          strings.ilike(body.html.inner_text, '*recover your*'),\n          strings.ilike(body.html.inner_text, '*unusual activity*'),\n          strings.ilike(body.html.inner_text, '*suspicious transaction*'),\n          strings.ilike(body.html.inner_text, '*transaction history*'),\n          strings.ilike(body.html.inner_text, '*please ignore this*'),\n          strings.ilike(body.html.inner_text, '*report activity*'),\n        )"
     macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n             .name == \"callback_scam\"\n      )"
  or
    body.html.display_text match "delivery note"
    body.html.display_text match "made with sumup"
  sender.email.domain.root_domain eq "sumup.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • note from.{0,50}(?:call|reach|contact|paypal) corpus 4 (mql 4)
field:"body.current_thread.text" kind:regex_match value:"note from.{0,50}(?:call|reach|contact|paypal)"
body.html.display_textwildcard
  • *delivery note*
  • *made with sumup*
field:"body.html.display_text" kind:wildcard
body.html.inner_textregex_match
    • +𝟭
    • 𝗽𝗮𝘆𝗺𝗲𝗻𝘁
    • 𝗛𝗲𝗹𝗽 𝗗𝗲𝘀𝗸
    • 𝗿𝗲𝗳𝘂𝗻𝗱
    • 𝗮𝗻𝘁𝗶𝘃𝗶𝗿𝘂𝘀
    • 𝗰𝗮𝗹𝗹
    • 𝗰𝗮𝗻𝗰𝗲𝗹
    corpus 5 (mql 5)
field:"body.html.inner_text" kind:regex_match
body.html.inner_textwildcard
  • *kindly* corpus 4 (mql 4)
field:"body.html.inner_text" kind:wildcard value:"*kindly*"
sender.email.domain.root_domainin
  • sumup.com
field:"sender.email.domain.root_domain" kind:in value:"sumup.com"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback phishing: Zero-width character obfuscation from freemail sender

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

Detects inbound messages sent from free email providers that contain a high volume of zero-width no-break space characters inserted before closing HTML tags, a technique used to break up and obfuscate text from content scanners. The rule also requires the presence of a phone number pattern in the message thread, consistent with callback phishing lures that rely on victims dialing a number rather than clicking a link.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesFree email provider, Evasion, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • body.html
  • sender.email
  • type

Rule Body

type.inbound
and sender.email.domain.root_domain in $free_email_providers
and regex.icount(body.html.raw, '\x{FEFF}+</[^>]*>[a-z0-9]') >= 20
and regex.icontains(body.current_thread.text,
                    '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                    '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
)

Detection Logic

Scope: inbound message.

Detects inbound messages sent from free email providers that contain a high volume of zero-width no-break space characters inserted before closing HTML tags, a technique used to break up and obfuscate text from content scanners. The rule also requires the presence of a phone number pattern in the message thread, consistent with callback phishing lures that rely on victims dialing a number rather than clicking a link.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. regex.icount(body.html.raw, '\x{FEFF}+</[^>]*>[a-z0-9]') >= 20
  4. body.current_thread.text matches any of 2 patterns
    • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
    • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}

Inspects: body.current_thread.text, body.html.raw, sender.email.domain.root_domain, type.inbound. Sensors: regex.icontains, regex.icount. Reference lists: $free_email_providers.

Indicators Matched (3)

FieldMatchValue
regex.icountregex\x{FEFF}+</[^>]*>[a-z0-9]
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  or
    body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
    body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
  regex.icount func_call "regex.icount(body.html.raw, '\\x{FEFF}+</[^>]*>[a-z0-9]') >= 20"
  sender.email.domain.root_domain macro "sender.email.domain.root_domain in free_email_providers"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4} corpus 9 (mql 9)
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4} corpus 9 (mql 9)
field:"body.current_thread.text" kind:regex_match
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Callback Scam: Outlook groups

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

Detects inbound messages sent to Outlook group distribution lists (groups.outlook.com) where the body text is classified with high confidence as callback scam intent by an NLU model, while excluding cases where recipient lists are empty or entirely invalid.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesSocial engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • recipients
  • recipients.to
  • type

Rule Body

type.inbound
and all(recipients.to, .email.domain.domain == "groups.outlook.com")
and any(ml.nlu_classifier(body.current_thread.text).intents,
        .name == "callback_scam" and .confidence == "high"
)
and not (
  length(recipients.to) == 0 or all(recipients.to, .email.domain.valid == false)
)

Detection Logic

Scope: inbound message.

Detects inbound messages sent to Outlook group distribution lists (groups.outlook.com) where the body text is classified with high confidence as callback scam intent by an NLU model, while excluding cases where recipient lists are empty or entirely invalid.

  1. inbound message
  2. all of recipients.to where:
    • .email.domain.domain is 'groups.outlook.com'
  3. any(ml.nlu_classifier(body.current_thread.text).intents, .name == "callback_scam" and .confidence == "high" )
  4. none of:
    • length(recipients.to) == 0
    • all of recipients.to where:
      • .email.domain.valid is False

Inspects: body.current_thread.text, recipients.to, recipients.to[].email.domain.domain, recipients.to[].email.domain.valid, type.inbound. Sensors: ml.nlu_classifier.

Indicators Matched (3)

FieldMatchValue
recipients.to[].email.domain.domainequalsgroups.outlook.com
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscallback_scam
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  not
    or
      recipients.to length_compare "0"
       macro "all(recipients.to, .email.domain.valid == false)"
  type.inbound eq "true"
   macro "all(recipients.to, .email.domain.domain == \"groups.outlook.com\")"
   macro "any(ml.nlu_classifier(body.current_thread.text).intents,\n        .name == \"callback_scam\" and .confidence == \"high\"\n)"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
recipients.tolength_compare0excludes:recipients.to field:"recipients.to" value:"0"

Service abuse: Callback phishing via Microsoft Teams invite

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

Detects abuse of legitimate Microsoft Teams invites containing callback scam content, including brand references and financial transaction language with phone numbers.

Threat Classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesImpersonation: Brand, Out of band pivot, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • body.links
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and sender.email.domain.domain == "teams.mail.microsoft"
// MS Teams invite indicators
and (
  any(body.links,
      .display_text == "Open Microsoft Teams"
      and (
        .href_url.domain.domain == "login.microsoftonline.com"
        or strings.iends_with(.href_url.query_params,
                              "login.microsoftonline.com"
        )
      )
  )
)
and (
  (
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    (
                      "mcafee|norton|geek.{0,5}squad|pay.?pal|ebay|symantec|best buy|lifelock|(ms|microsoft|teams).{0,10}premium"
                    )
    )
    or 3 of (
      strings.ilike(body.current_thread.text, '*purchase*'),
      strings.ilike(body.current_thread.text, '*p?ym?nt*'),
      strings.ilike(body.current_thread.text, '*transaction*'),
      strings.ilike(body.current_thread.text, '*subscription*'),
      strings.ilike(body.current_thread.text, '*antivirus*'),
      strings.ilike(body.current_thread.text, '*order*'),
      strings.ilike(body.current_thread.text, '*support*'),
      strings.ilike(body.current_thread.text, '*help line*'),
      strings.ilike(body.current_thread.text, '*receipt*'),
      strings.ilike(body.current_thread.text, '*c?ntact*'),
      strings.ilike(body.current_thread.text, '*cancel*'),
      strings.ilike(body.current_thread.text, '*renew*'),
      strings.ilike(body.current_thread.text, '*refund*'),
      strings.ilike(body.current_thread.text, '*billing*'),
      regex.icontains(body.current_thread.text, '[li]nv.[li]ce')
    )
  )
  // phone number regex
  and any([body.current_thread.text, subject.subject],
          regex.icontains(.,
                          '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}'
          )
  )
)

Detection Logic

Scope: inbound message.

Detects abuse of legitimate Microsoft Teams invites containing callback scam content, including brand references and financial transaction language with phone numbers.

  1. inbound message
  2. sender.email.domain.domain is 'teams.mail.microsoft'
  3. any of body.links where all hold:
    • .display_text is 'Open Microsoft Teams'
    • any of:
      • .href_url.domain.domain is 'login.microsoftonline.com'
      • .href_url.query_params ends with 'login.microsoftonline.com'
  4. all of:
    • any of:
      • regex.icontains(strings.replace_confusables(body.current_thread.text), ( "mcafee|norton|geek.{0,5}squad|pay.?pal|ebay|symantec|best buy|lifelock|(ms|microsoft|teams).{0,10}premium" ) )
      • at least 3 of 15: body.current_thread.text matches any of 15 patterns
        • *purchase*
        • *p?ym?nt*
        • *transaction*
        • *subscription*
        • *antivirus*
        • *order*
        • *support*
        • *help line*
        • *receipt*
        • *c?ntact*
        • *cancel*
        • *renew*
        • *refund*
        • *billing*
        • [li]nv.[li]ce
    • any([body.current_thread.text, subject.subject], regex.icontains(., '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}' ) )

Inspects: body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.domain.domain, body.links[].href_url.query_params, sender.email.domain.domain, subject.subject, type.inbound. Sensors: regex.icontains, strings.iends_with, strings.ilike, strings.replace_confusables.

Indicators Matched (21)

FieldMatchValue
sender.email.domain.domainequalsteams.mail.microsoft
body.links[].display_textequalsOpen Microsoft Teams
body.links[].href_url.domain.domainequalslogin.microsoftonline.com
strings.iends_withsuffixlogin.microsoftonline.com
regex.icontainsregexmcafee|norton|geek.{0,5}squad|pay.?pal|ebay|symantec|best buy|lifelock|(ms|microsoft|teams).{0,10}premium
strings.ilikesubstring*purchase*
strings.ilikesubstring*p?ym?nt*
strings.ilikesubstring*transaction*
strings.ilikesubstring*subscription*
strings.ilikesubstring*antivirus*
strings.ilikesubstring*order*
strings.ilikesubstring*support*
9 more
strings.ilikesubstring*help line*
strings.ilikesubstring*receipt*
strings.ilikesubstring*c?ntact*
strings.ilikesubstring*cancel*
strings.ilikesubstring*renew*
strings.ilikesubstring*refund*
strings.ilikesubstring*billing*
regex.icontainsregex[li]nv.[li]ce
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      or
        body.links.href_url.domain.domain eq "login.microsoftonline.com"
        body.links.href_url.query_params ends_with "login.microsoftonline.com"
      body.links.display_text eq "Open Microsoft Teams"
  or
    regex.icontains func_call "regex.icontains(strings.replace_confusables(body.current_thread.text),\n                    (\n                      \"mcafee|norton|geek.{0,5}squad|pay.?pal|ebay|symantec|best buy|lifelock|(ms|microsoft|teams).{0,10}premium\"\n                    )\n    )"
     macro "3 of (\n      strings.ilike(body.current_thread.text, '*purchase*'),\n      strings.ilike(body.current_thread.text, '*p?ym?nt*'),\n      strings.ilike(body.current_thread.text, '*transaction*'),\n      strings.ilike(body.current_thread.text, '*subscription*'),\n      strings.ilike(body.current_thread.text, '*antivirus*'),\n      strings.ilike(body.current_thread.text, '*order*'),\n      strings.ilike(body.current_thread.text, '*support*'),\n      strings.ilike(body.current_thread.text, '*help line*'),\n      strings.ilike(body.current_thread.text, '*receipt*'),\n      strings.ilike(body.current_thread.text, '*c?ntact*'),\n      strings.ilike(body.current_thread.text, '*cancel*'),\n      strings.ilike(body.current_thread.text, '*renew*'),\n      strings.ilike(body.current_thread.text, '*refund*'),\n      strings.ilike(body.current_thread.text, '*billing*'),\n      regex.icontains(body.current_thread.text, '[li]nv.[li]ce')\n    )"
  sender.email.domain.domain eq "teams.mail.microsoft"
  type.inbound eq "true"
   macro "any([body.current_thread.text, subject.subject],\n          regex.icontains(.,\n                          '\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}'\n          )\n  )"

Indicators

These rows show field, operator, and value matches.

Back to all Sublime MQL rules