Detection rules › Sublime MQL

Extortion / sextortion (untrusted sender)

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

Detects extortion and sextortion attempts by analyzing the email body text from an untrusted sender.

Threat classification

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

CategoryValues
Attack typesExtortion
Tactics and techniquesSocial engineering, Spoofing

Event coverage

Rule body MQL

type.inbound
and length(filter(body.links, .display_text is not null)) < 10
and not (
  ml.nlu_classifier(body.current_thread.text).language == "english"
  and any(ml.nlu_classifier(body.html.display_text).topics,
          .name in (
            "News and Current Events",
            "Newsletters and Digests",
            "Advertising and Promotions"
          )
          and .confidence in ("high", "medium")
  )
)
and (
  (
    any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents,
        (.name == "extortion" and .confidence == "high")
    )
    and (
      any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text
                            )
          ).entities,
          .name == "financial"
          or (
            .name is not null
            and regex.icontains(.text,
                                "cybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s"
            )
          )
      )
      or any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text
                               )
             ).topics,
             .name == "Financial Communications" and .confidence != "low"
      )
    )
  )
  // catches extortion content delivered as inline base64 images
  or (
    (body.current_thread.text is null or length(body.current_thread.text) < 10)
    and any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents,
            .name == "extortion" and .confidence == "high"
    )
    and (
      any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities,
          .name == "financial"
      )
      or any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics,
             .name == "Financial Communications" and .confidence != "low"
      )
    )
  )
  // manual indicators failsafe
  or 3 of (
    // malware terms
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    "(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)"
    ),
    // actions recorded
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    "(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)"
    ),
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    "(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)"
    ),
    // a timeframe to pay
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    '[ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])',
                    '(?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?',
                    'set a timer'
    ),
    // a promise from the actor
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    '(?:pe[rŗ]manently|will|I''ll) delete|([rŗ]emove|destroy) (?:[\p{L}\p{M}\p{N}]+\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)'
    ),
    // a threat from the actor
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    '(?:\bsen[dt]|forward|expose|share)\s*(?:[\p{L}\p{N}]+\s*){0,5}\s*to\s*(?:[\p{L}\p{N}]+\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\b)'
    ),
    // bitcoin language (excluding newsletters)
    (
      regex.icontains(strings.replace_confusables(body.current_thread.text),
                      '[bḆḂ]it[cĉƈ][oöة]i[nņɲ]|\bbtc\b|blockchain'
      )
      // negate cryptocurrency newsletters
      and not (
        any(body.links,
            strings.icontains(.display_text, "unsubscribe")
            and (
              strings.icontains(.href_url.path, "unsubscribe")
              // handle mimecast URL rewrites
              or (
                .href_url.domain.root_domain == 'mimecastprotect.com'
                and strings.icontains(.href_url.query_params,
                                      sender.email.domain.root_domain
                )
              )
            )
        )
      )
    ),
    (
      regex.icontains(strings.replace_confusables(body.current_thread.text),
                      '(?:contact the police|(?:bitcoin|\bbtc\b).{0,20}(?:wallet|address))'
      )
      and regex.icontains(strings.replace_confusables(body.current_thread.text),
                          '(?:\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\b)|\bX[1-9A-HJ-NP-Za-km-z]{33}\b|\b(?:0x[a-fA-F0-9]{40})\b|\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\b|\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b'
      )
    ),
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    'bc1q.{0,50}\b'
    ),
    (
      regex.count(body.current_thread.text,
                  '[\x{0300}-\x{036F}\x{1AB0}-\x{1AFF}\x{1DC0}-\x{1DFF}\x{0100}-\x{024F}\x{1E00}-\x{1EFF}]'
      ) > 20
      and length(body.current_thread.links) == 0
    )
  )
)
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
  or any(headers.hops, any(.fields, .name == "X-Google-Group-Id"))

  // many extortion emails spoof sender domains and fail sender authentication
  or (
    not headers.auth_summary.dmarc.pass
    or headers.auth_summary.dmarc.pass is null
    or not headers.auth_summary.spf.pass
  )
)
// negate legit bounce backs
and not any(ml.nlu_classifier(body.current_thread.text).topics,
            .name in ("Bounce Back and Delivery Failure Notifications")
)
// negate legit forwards and replies
and not (
  (subject.is_reply or subject.is_forward)
  and length(body.previous_threads) > 0
  and (length(headers.references) > 0 or headers.in_reply_to is not null)
)
// negate benign newsletters that mention cyber extortion
and not (
  any(body.links,
      strings.icontains(.display_text, "unsubscribe")
      and strings.icontains(.href_url.path, "unsubscribe")
  )
  // newsletters are typically longer than the average extortion script
  and length(body.current_thread.text) > 2000
)
and length(body.current_thread.text) < 8000
// 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.

Detects extortion and sextortion attempts by analyzing the email body text from an untrusted sender.

  1. inbound message
  2. length(filter(body.links, .display_text is not null)) < 10
  3. not:
    • all of:
      • ml.nlu_classifier(body.current_thread.text).language is 'english'
      • any of ml.nlu_classifier(body.html.display_text).topics where all hold:
        • .name in ('News and Current Events', 'Newsletters and Digests', 'Advertising and Promotions')
        • .confidence in ('high', 'medium')
  4. any of:
    • all of:
      • any of ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents where all hold:
        • .name is 'extortion'
        • .confidence is 'high'
      • any of:
        • any of ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities where any holds:
          • .name is 'financial'
          • all of:
            • .name is set
            • .text matches 'cybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s'
        • any of ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics where all hold:
          • .name is 'Financial Communications'
          • .confidence is not 'low'
    • all of:
      • any of:
        • body.current_thread.text is missing
        • length(body.current_thread.text) < 10
      • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents where all hold:
        • .name is 'extortion'
        • .confidence is 'high'
      • any of:
        • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities where:
          • .name is 'financial'
        • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics where all hold:
          • .name is 'Financial Communications'
          • .confidence is not 'low'
    • at least 3 of:
      • strings.replace_confusables(body.current_thread.text) matches '(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)'
      • strings.replace_confusables(body.current_thread.text) matches any of 3 patterns
        • [ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])
        • (?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?
        • set a timer
      • strings.replace_confusables(body.current_thread.text) matches "(?:pe[rŗ]manently|will|I'll) delete|([rŗ]emove|destroy) (?:[\\p{L}\\p{M}\\p{N}]+\\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)"
      • strings.replace_confusables(body.current_thread.text) matches '(?:\\bsen[dt]|forward|expose|share)\\s*(?:[\\p{L}\\p{N}]+\\s*){0,5}\\s*to\\s*(?:[\\p{L}\\p{N}]+\\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\\b)'
      • all of:
        • strings.replace_confusables(body.current_thread.text) matches '[bḆḂ]it[cĉƈ][oöة]i[nņɲ]|\\bbtc\\b|blockchain'
        • not:
          • any of body.links where all hold:
            • .display_text contains 'unsubscribe'
            • any of:
              • .href_url.path contains 'unsubscribe'
              • all of:
                • .href_url.domain.root_domain is 'mimecastprotect.com'
                • strings.icontains(.href_url.query_params)
      • all of:
        • strings.replace_confusables(body.current_thread.text) matches '(?:contact the police|(?:bitcoin|\\bbtc\\b).{0,20}(?:wallet|address))'
        • strings.replace_confusables(body.current_thread.text) matches '(?:\\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\\b)|\\bX[1-9A-HJ-NP-Za-km-z]{33}\\b|\\b(?:0x[a-fA-F0-9]{40})\\b|\\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\\b|\\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\\b'
      • strings.replace_confusables(body.current_thread.text) matches 'bc1q.{0,50}\\b'
      • all of:
        • regex.count(body.current_thread.text, '[\\x{0300}-\\x{036F}\\x{1AB0}-\\x{1AFF}\\x{1DC0}-\\x{1DFF}\\x{0100}-\\x{024F}\\x{1E00}-\\x{1EFF}]') > 20
        • length(body.current_thread.links) is 0
  5. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
    • any of headers.hops where:
      • any of .fields where:
        • .name is 'X-Google-Group-Id'
    • any of:
      • not:
        • headers.auth_summary.dmarc.pass
      • headers.auth_summary.dmarc.pass is missing
      • not:
        • headers.auth_summary.spf.pass
  6. not:
    • any of ml.nlu_classifier(body.current_thread.text).topics where:
      • .name in ('Bounce Back and Delivery Failure Notifications')
  7. not:
    • all of:
      • any of:
        • subject.is_reply
        • subject.is_forward
      • length(body.previous_threads) > 0
      • any of:
        • length(headers.references) > 0
        • headers.in_reply_to is set
  8. not:
    • all of:
      • any of body.links where all hold:
        • .display_text contains 'unsubscribe'
        • .href_url.path contains 'unsubscribe'
      • length(body.current_thread.text) > 2000
  9. length(body.current_thread.text) < 8000
  10. 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: body.current_thread.links, body.current_thread.text, body.html.display_text, body.links, body.links[].display_text, body.links[].href_url.domain.root_domain, body.links[].href_url.path, body.links[].href_url.query_params, body.previous_threads, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.is_forward, subject.is_reply, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.nlu_classifier, profile.by_sender, regex.count, regex.icontains, strings.icontains, strings.replace_confusables. Reference lists: $high_trust_sender_root_domains.

Indicators matched (31)

FieldMatchValue
ml.nlu_classifier(body.html.display_text).topics[].namememberNews and Current Events
ml.nlu_classifier(body.html.display_text).topics[].namememberNewsletters and Digests
ml.nlu_classifier(body.html.display_text).topics[].namememberAdvertising and Promotions
ml.nlu_classifier(body.html.display_text).topics[].confidencememberhigh
ml.nlu_classifier(body.html.display_text).topics[].confidencemembermedium
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents[].nameequalsextortion
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents[].confidenceequalshigh
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities[].nameequalsfinancial
regex.icontainsregexcybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics[].nameequalsFinancial Communications
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].nameequalsextortion
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].confidenceequalshigh
19 more
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities[].nameequalsfinancial
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics[].nameequalsFinancial Communications
regex.icontainsregex(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)
regex.icontainsregex(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)
regex.icontainsregex(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)
regex.icontainsregex[ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])
regex.icontainsregex(?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?
regex.icontainsregexset a timer
regex.icontainsregex(?:pe[rŗ]manently|will|I'll) delete|([rŗ]emove|destroy) (?:[\p{L}\p{M}\p{N}]+\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)
regex.icontainsregex(?:\bsen[dt]|forward|expose|share)\s*(?:[\p{L}\p{N}]+\s*){0,5}\s*to\s*(?:[\p{L}\p{N}]+\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\b)
regex.icontainsregex[bḆḂ]it[cĉƈ][oöة]i[nņɲ]|\bbtc\b|blockchain
strings.icontainssubstringunsubscribe
body.links[].href_url.domain.root_domainequalsmimecastprotect.com
regex.icontainsregex(?:contact the police|(?:bitcoin|\bbtc\b).{0,20}(?:wallet|address))
regex.icontainsregex(?:\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\b)|\bX[1-9A-HJ-NP-Za-km-z]{33}\b|\b(?:0x[a-fA-F0-9]{40})\b|\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\b|\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b
regex.icontainsregexbc1q.{0,50}\b
regex.countregex[\x{0300}-\x{036F}\x{1AB0}-\x{1AFF}\x{1DC0}-\x{1DFF}\x{0100}-\x{024F}\x{1E00}-\x{1EFF}]
headers.hops[].fields[].nameequalsX-Google-Group-Id
ml.nlu_classifier(body.current_thread.text).topics[].namememberBounce Back and Delivery Failure Notifications