• Sublime Core Feed
High Severity

Callback Phishing via Xodo Sign comment

Labels

Callback Phishing
Exploit
Impersonation: Brand
Out of band pivot
Social engineering
Computer Vision
Content analysis
Header analysis
Sender analysis
URL analysis

Description

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.

References

No references.

Sublime Security
Created Apr 28th, 2025 • Last updated Jul 16th, 2025
Feed Source
Sublime Core Feed
Source
GitHub
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(beta.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 (
    any(file.explode(beta.message_screenshot()),
        // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
        // and above for current_thread.text
        3 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'),
          regex.icontains(.scan.ocr.raw, "(?:reach|contact) us at"),
          strings.icontains(.scan.ocr.raw, '+1'),
          strings.icontains(.scan.ocr.raw, 'amount'),
          strings.icontains(.scan.ocr.raw, 'charged'),
          strings.icontains(.scan.ocr.raw, 'crypto'),
          strings.icontains(.scan.ocr.raw, 'wallet address'),
          regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b'),
        )
        // phone number regex
        and regex.icontains(.scan.ocr.raw,
                            '\+?([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(.scan.ocr.raw, '(?:from|to|sent|date|cc|subject):') > 3
        // this notation of previous threads often only occurs once
        and not regex.icontains(.scan.ocr.raw, 'wrote:[\r\n]')
    )
  )
)
MQL Rule Console
DocsLearning Labs

Playground

Test against your own EMLs or sample data.

Share

Post about this on your socials.

Get Started. Today.

Managed or self-managed. No MX changes.

Get Started