Detection rules › Sublime MQL

Reconnaissance: Large unknown recipient list

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

Recon messages, a form of deliverability testing, are used to validate whether a recipient address is valid or not, potentially preceding an attack. There's a large number of recipients that are unknown to the organization, no links or attachments, and a short body and subject from an unknown sender.

Threat classification

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

CategoryValues
Attack typesReconnaissance

Event coverage

Rule body MQL

type.inbound
and (
  length(recipients.to) > 10
  and length(filter(recipients.to,
                    .email.domain.domain not in $org_domains
                    and .email.email not in $recipient_emails
                    and (
                      .email.domain.valid
                      or strings.icontains(.display_name, "undisclosed")
                    )
             )
  ) >= 10
)
and (
  length(subject.subject) <= 10 
  or subject.subject == body.current_thread.text
  or (subject.is_reply and length(body.previous_threads) == 0)
)
and (
  length(body.links) == 0
  or length(filter(body.links,
                   (
                     .display_text is null
                     and .display_url.url == sender.email.domain.root_domain
                   )
                   or .href_url.domain.domain == "aka.ms"
                   or network.whois(.display_url.domain).days_old < 30
            )
  ) == length(body.links)
)
and (
  length(attachments) == 0
  or (
    length(attachments) == 1
    and any(attachments,
            .file_type in ("pdf", "png", "jpg", "tif", "heif", "doc", "docx")
            and any(file.explode(.),
                    length(.scan.ocr.raw) < 20
                    or length(.scan.strings.strings) == 1
            )
    )
  )
)
and (
  body.current_thread.text is null
  or length(body.current_thread.text) < 50
  // body length without disclaimer is shorter than 50 characters
  or (
    any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
                   .name == "disclaimer"
            ),
            .text
        ),
        (length(body.current_thread.text) - length(.)) < 50
    )
  )
)
and profile.by_sender().prevalence != "common"
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.

Recon messages, a form of deliverability testing, are used to validate whether a recipient address is valid or not, potentially preceding an attack. There's a large number of recipients that are unknown to the organization, no links or attachments, and a short body and subject from an unknown sender.

  1. inbound message
  2. all of:
    • length(recipients.to) > 10
    • length(filter(recipients.to, .email.domain.domain not in $org_domains and .email.email not in $recipient_emails and .email.domain.valid or strings.icontains(.display_name, 'undisclosed'))) ≥ 10
  3. any of:
    • length(subject.subject) ≤ 10
    • subject.subject is body.current_thread.text
    • all of:
      • subject.is_reply
      • length(body.previous_threads) is 0
  4. any of:
    • length(body.links) is 0
    • length(filter(body.links, .display_text is null and .display_url.url == sender.email.domain.root_domain or .href_url.domain.domain == 'aka.ms' or network.whois(.display_url.domain).days_old < 30)) is length(body.links)
  5. any of:
    • length(attachments) is 0
    • all of:
      • length(attachments) is 1
      • any of attachments where all hold:
        • .file_type in ('pdf', 'png', 'jpg', 'tif', 'heif', 'doc', 'docx')
        • any of file.explode(.) where any holds:
          • length(.scan.ocr.raw) < 20
          • length(.scan.strings.strings) is 1
  6. any of:
    • body.current_thread.text is missing
    • length(body.current_thread.text) < 50
    • any of map(...) where:
      • length(body.current_thread.text) - length(.) < 50
  7. profile.by_sender().prevalence is not 'common'
  8. not:
    • profile.by_sender().solicited
  9. not:
    • profile.by_sender().any_messages_benign
  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: attachments[].file_type, body.current_thread.text, body.links, body.links[].display_text, body.links[].display_url.domain, body.links[].display_url.url, body.links[].href_url.domain.domain, body.previous_threads, headers.auth_summary.dmarc.pass, recipients.to, recipients.to[].display_name, recipients.to[].email.domain.domain, recipients.to[].email.domain.valid, recipients.to[].email.email, sender.email.domain.root_domain, subject.is_reply, subject.subject, type.inbound. Sensors: file.explode, ml.nlu_classifier, network.whois, profile.by_sender, strings.icontains. Reference lists: $high_trust_sender_root_domains, $org_domains, $recipient_emails.

Indicators matched (10)

FieldMatchValue
strings.icontainssubstringundisclosed
body.links[].href_url.domain.domainequalsaka.ms
attachments[].file_typememberpdf
attachments[].file_typememberpng
attachments[].file_typememberjpg
attachments[].file_typemembertif
attachments[].file_typememberheif
attachments[].file_typememberdoc
attachments[].file_typememberdocx
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsdisclaimer