Reconnaissance: All recipients cc/bcc'd or undisclosed

Recon messages, a form of deliverability testing, are used to validate whether a recipient address is valid or not, potentially preceding an attack.

All recipients are bcc'd or undisclosed, with no links or attachments, and a short body and subject from an unknown sender.

Sublime rule (View on GitHub)

 1name: "Reconnaissance: All recipients cc/bcc'd or undisclosed"
 2description: |
 3  Recon messages, a form of deliverability testing, are used to validate whether a recipient address is valid or not, potentially preceding an attack.
 4
 5  All recipients are bcc'd or undisclosed, with no links or attachments, and a short body and subject from an unknown sender.  
 6type: "rule"
 7severity: "low"
 8source: |
 9  type.inbound
10  and (
11    length(recipients.bcc) > 0
12    or length(recipients.cc) > 0
13    or any(recipients.to, strings.ilike(.display_name, "undisclosed?recipients"))
14  )
15  and (
16    length(subject.subject) <= 10
17    // extract the "real subject" (strip off any external warning keywords)
18    or any(regex.iextract(subject.subject,
19                          '(?:^\[?(?:EXT|EXTERNAL)\]?[: ]\s*){0,3} ?(?P<real_subject>.*)'
20           ),
21           length(.named_groups['real_subject']) <= 10
22    )
23    or (
24      strings.ilike(subject.subject, "*checking*", "*testing*")
25      and length(subject.subject) <= 25
26    )
27  )
28  and length(attachments) == 0
29  // and there are no links. Or all the links are to aka.ms or an extraction from a warning banner that match the senders domain
30  and (
31    length(body.links) == 0
32    or length(filter(body.links,
33                     (
34                       .display_text is null
35                       and .display_url.url == sender.email.domain.root_domain
36                     )
37                     or .href_url.domain.domain == "aka.ms"
38              )
39    ) == length(body.links)
40  )
41  and (
42    body.current_thread.text is null
43    or length(body.current_thread.text) < 50
44    or (
45      length(body.current_thread.text) < 900
46      // or body is most likely all warning banner ending with a generic greeting
47      and regex.imatch(body.current_thread.text, '.*(hi|hello)')
48    )
49    // body length without disclaimer is shorter than 50 characters
50    or (
51      any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
52                     .name == "disclaimer"
53              ),
54              .text
55          ),
56          (length(body.current_thread.text) - length(.)) < 50
57      )
58    )
59  )
60  and profile.by_sender().prevalence != "common"
61  and not profile.by_sender().solicited
62  and not profile.by_sender().any_false_positives
63  
64  // negate highly trusted sender domains unless they fail DMARC authentication
65  and (
66    (
67      sender.email.domain.root_domain in $high_trust_sender_root_domains
68      and not headers.auth_summary.dmarc.pass
69    )
70    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
71  )  
72
73tags:
74  - "Attack surface reduction"
75  - "Deliverability testing"
76attack_types:
77  - "Reconnaissance"
78detection_methods:
79  - "Content analysis"
80  - "Header analysis"
81  - "Sender analysis"
82id: "420f60d3-5d10-5384-9253-9521a758e799"

Related rules

to-top