Credential Phishing: Fake Password Expiration from New and Unsolicited sender

This rule looks for password expiration verbiage in the subject and body. Requiring between 1 - 9 links, a short body, and NLU in addition to statically specified term anchors. High trust senders are also negated.

Sublime rule (View on GitHub)

  1name: "Credential Phishing: Fake Password Expiration from New and Unsolicited sender"
  2description: "This rule looks for password expiration verbiage in the subject and body. Requiring between 1 - 9 links, a short body, and NLU in addition to statically specified term anchors. High trust senders are also negated."
  3type: "rule"
  4severity: "medium"
  5source: |
  6  type.inbound
  7  
  8  // few links which are not in $org_domains
  9  and 0 < length(filter(body.links, .href_url.domain.domain not in $org_domains)) <= 10
 10  
 11  // no attachments or suspicious attachment
 12  and (
 13    length(attachments) == 0
 14    or any(attachments,
 15           .file_type in ("pdf", "doc", "docx")
 16           and any(file.explode(.),
 17                   .scan.entropy.entropy > 7 and length(.scan.ocr.raw) < 20
 18           )
 19    )
 20    // or there are duplicate pdfs in name 
 21    or (
 22      length(filter(attachments, .file_type == "pdf")) > length(distinct(filter(attachments,
 23                                                                                .file_type == "pdf"
 24                                                                         ),
 25                                                                         .file_name
 26                                                                )
 27      )
 28      or 
 29      // all PDFs are the same MD5
 30      length(distinct(filter(attachments, .file_type == "pdf"), .md5)) == 1
 31    )
 32  )
 33  
 34  // body contains expire, expiration, loose, lose 
 35  and (
 36    regex.icontains(body.current_thread.text,
 37                    '(expir(e)?(ation|s)|\blo(o)?se\b|(?:offices?|microsoft).365|re.{0,3}confirm)|due for update'
 38    )
 39    and not strings.icontains(body.current_thread.text, 'link expires in ')
 40  )
 41  and (
 42    // subject or body contains account or access
 43    any([subject.subject, body.current_thread.text],
 44        regex.icontains(., "account|access|your email")
 45    )
 46    // suspicious use of recipients email address
 47    or any(recipients.to,
 48           any([subject.subject, body.current_thread.text],
 49               strings.icontains(strings.replace_confusables(.),
 50                                 ..email.local_part
 51               )
 52               or strings.icontains(strings.replace_confusables(.), ..email.email)
 53           )
 54    )
 55  )
 56  
 57  // subject or body must contains password
 58  and any([
 59            strings.replace_confusables(subject.subject),
 60            strings.replace_confusables(body.current_thread.text)
 61          ],
 62          regex.icontains(., '\bpassword\b', '\bmulti.?factor\b')
 63  )
 64  and (
 65    any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents,
 66        .name == "cred_theft" and .confidence == "high"
 67    )
 68    or length(filter([
 69                       "password",
 70                       "expiration",
 71                       "expire",
 72                       "expiring",
 73                       "kindly",
 74                       "renew",
 75                       "review",
 76                       "click below",
 77                       "kicked out",
 78                       "prevent",
 79                       "required now",
 80                       "immediate action",
 81                       "security update",
 82                       "blocked",
 83                       "locked",
 84                       "interruption",
 85                       "stay connected",
 86                     ],
 87                     strings.icontains(strings.replace_confusables(body.current_thread.text
 88                                       ),
 89                                       .
 90                     )
 91              )
 92    ) >= 3
 93  )
 94  
 95  // body length between 200 and 2000
 96  and (
 97    200 < length(body.current_thread.text) < 2000
 98  
 99    // excessive whitespace
100    or (
101      regex.icontains(body.html.raw, '(?:(?:<br\s*/?>\s*){20,}|\n{20,})')
102      or regex.icontains(body.html.raw, '(?:<p[^>]*>\s*<br\s*/?>\s*</p>\s*){30,}')
103      or regex.icontains(body.html.raw,
104                         '(?:<p class=".*?"><span style=".*?"><o:p>&nbsp;</o:p></span></p>\s*){30,}'
105      )
106      or regex.icontains(body.html.raw, '(?:<p>\s*&nbsp;\s*</p>\s*){7,}')
107      or regex.icontains(body.html.raw, '(?:<p>\s*&nbsp;\s*</p>\s*<br>\s*){7,}')
108      or regex.icontains(body.html.raw,
109                         '(?:<p[^>]*>\s*&nbsp;\s*<br>\s*</p>\s*){5,}'
110      )
111      or regex.icontains(body.html.raw, '(?:<p[^>]*>&nbsp;</p>\s*){7,}')
112    )
113  )
114  
115  // a body link does not match the sender domain
116  and any(body.links,
117          .href_url.domain.root_domain != sender.email.domain.root_domain
118          and .href_url.domain.root_domain not in $org_domains
119  )
120  
121  // and no false positives and not solicited
122  and (
123    not profile.by_sender().any_false_positives
124    and not profile.by_sender().solicited
125  )
126  
127  // not a reply
128  and (
129    length(headers.references) == 0
130    or not any(headers.hops, any(.fields, strings.ilike(.name, "In-Reply-To")))
131  )
132  
133  // negate highly trusted sender domains unless they fail DMARC authentication
134  and (
135    (
136      sender.email.domain.root_domain in $high_trust_sender_root_domains
137      and (
138        any(distinct(headers.hops, .authentication_results.dmarc is not null),
139            strings.ilike(.authentication_results.dmarc, "*fail")
140        )
141      )
142    )
143    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
144  )  
145
146attack_types:
147  - "Credential Phishing"
148tactics_and_techniques:
149  - "Social engineering"
150detection_methods:
151  - "Content analysis"
152  - "Natural Language Understanding"
153  - "Sender analysis"
154id: "5d9c3a75-5f57-5d0c-a07f-0f300bbde076"
to-top