Detection rules › Sublime MQL

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

Event coverage

Rule body MQL

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) is 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:
    • strings.replace_confusables(body.current_thread.text) matches '(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 of ml.logo_detect(file.message_screenshot()).brands where:
      • .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:
        • beta.ocr(file.message_screenshot()).text contains 'purchase'
        • beta.ocr(file.message_screenshot()).text contains 'payment'
        • beta.ocr(file.message_screenshot()).text contains 'transaction'
        • beta.ocr(file.message_screenshot()).text contains 'subscription'
        • beta.ocr(file.message_screenshot()).text contains 'antivirus'
        • beta.ocr(file.message_screenshot()).text contains 'order'
        • beta.ocr(file.message_screenshot()).text contains 'support'
        • beta.ocr(file.message_screenshot()).text contains 'help line'
        • beta.ocr(file.message_screenshot()).text contains 'receipt'
        • beta.ocr(file.message_screenshot()).text contains 'invoice'
        • beta.ocr(file.message_screenshot()).text contains 'call'
        • beta.ocr(file.message_screenshot()).text contains 'helpdesk'
        • beta.ocr(file.message_screenshot()).text contains 'cancel'
        • beta.ocr(file.message_screenshot()).text contains 'renew'
        • beta.ocr(file.message_screenshot()).text contains 'refund'
        • beta.ocr(file.message_screenshot()).text matches '(?:reach|contact) us at'
        • beta.ocr(file.message_screenshot()).text contains '+1'
        • beta.ocr(file.message_screenshot()).text contains 'amount'
        • beta.ocr(file.message_screenshot()).text contains 'charged'
        • beta.ocr(file.message_screenshot()).text contains 'crypto'
        • beta.ocr(file.message_screenshot()).text contains 'wallet address'
        • beta.ocr(file.message_screenshot()).text matches '\\$\\d{3}\\.\\d{2}\\b'
      • beta.ocr(file.message_screenshot()).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}
      • not:
        • regex.icount(beta.ocr(file.message_screenshot()).text, '(?:from|to|sent|date|cc|subject):') > 3
      • not:
        • beta.ocr(file.message_screenshot()).text matches '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 (35)

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
23 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
regex.icountregex(?:from|to|sent|date|cc|subject):
regex.icontainsregexwrote:[\r\n]