Credential phishing: Engaging language and other indicators (untrusted sender)
Message contains various suspicious indicators as well as engaging language resembling credential theft from an untrusted sender.
Sublime rule (View on GitHub)
1name: "Credential phishing: Engaging language and other indicators (untrusted sender)"
2description: |
3 Message contains various suspicious indicators as well as engaging language resembling credential theft from an untrusted sender.
4type: "rule"
5severity: "medium"
6source: |
7 type.inbound
8 and (
9 regex.icontains(subject.subject,
10 "termination.*notice",
11 "38417",
12 ":completed",
13 "[il1]{2}mit.*ma[il1]{2} ?bo?x",
14 "[il][il][il]egai[ -]",
15 "[li][li][li]ega[li] attempt",
16 "[ng]-?[io]n .*block",
17 "[ng]-?[io]n .*cancel",
18 "[ng]-?[io]n .*deactiv",
19 "[ng]-?[io]n .*disabl",
20 "action.*required",
21 "abandon.*package",
22 "about.your.account",
23 "acc(ou)?n?t (is )?on ho[li]d",
24 "acc(ou)?n?t.*terminat",
25 "acc(oun)?t.*[il1]{2}mitation",
26 "access.*limitation",
27 "account (will be )?block",
28 "account.*de-?activat",
29 "account.*locked",
30 "account.*re-verification",
31 "account.*security",
32 "account.*suspension",
33 "account.has.expired",
34 "account.will.be.blocked",
35 "account v[il]o[li]at",
36 "activity.*acc(oun)?t",
37 "almost.full",
38 "app[li]e.[il]d",
39 "authenticate.*account",
40 "been.*suspend",
41 "crediential.*notif",
42 "clos.*of.*account.*processed",
43 "confirm.your.account",
44 "courier.*able",
45 "crediential.*notif",
46 "deactivation.*in.*progress",
47 "delivery.*attempt.*failed",
48 "disconnection.*notice",
49 "document.received",
50 "documented.*shared.*with.*you",
51 "dropbox.*document",
52 "e-?ma[il1]+ .{010}suspen",
53 "e-?ma[il1]{1} user",
54 "e-?ma[il1]{2} acc",
55 "e-?ma[il1]{2} preview",
56 "e-?ma[il1]{2}.*up.?grade",
57 "e.?ma[il1]{2}.*server",
58 "e.?ma[il1]{2}.*suspend",
59 "electronic advisory",
60 "email.update",
61 "faxed you",
62 "fraud(ulent)?.*charge",
63 "from.helpdesk",
64 "fu[il1]{2}.*ma[il1]+[ -]?box",
65 "has.been.*suspended",
66 "has.been.limited",
67 "have.locked",
68 "he[li]p ?desk upgrade",
69 "heipdesk",
70 "i[il]iega[il]",
71 "ii[il]ega[il]",
72 "incoming e?mail",
73 "incoming.*fax",
74 "lock.*security",
75 "ma[il1]{1}[ -]?box.*quo",
76 "ma[il1]{2}[ -]?box.*fu[il1]",
77 "ma[il1]{2}box.*[il1]{2}mit",
78 "ma[il1]{2}box stor",
79 "mail on.?hold",
80 "mail.*box.*migration",
81 "mail.*de-?activat",
82 "mail.update.required",
83 "mails.*pending",
84 "messages.*pending",
85 "missed.*shipping.*notification",
86 "missed.shipment.notification",
87 "must.update.your.account",
88 "new [sl][io]g?[nig][ -]?in from",
89 "new voice ?-?mail",
90 "notifications.*pending",
91 "office.*3.*6.*5.*suspend",
92 "office365",
93 "on google docs with you",
94 "online doc",
95 "outstanding inv\\b",
96 "password.*compromised",
97 "(?:payroll|salary|bonus).*Distribution",
98 "periodic maintenance",
99 "potential(ly)? unauthorized",
100 "refund not approved",
101 "report",
102 "\\brfq\\s_",
103 "revised.*policy",
104 "scam",
105 "scanned.?invoice",
106 "secured?.update",
107 "security breach",
108 "securlty",
109 "signed.*delivery",
110 "status of your .{314}? ?delivery",
111 "susp[il1]+c[il1]+ous.*act[il1]+v[il1]+ty",
112 "suspicious.*sign.*[io]n",
113 "suspicious.activit",
114 "temporar(il)?y deactivate",
115 "temporar[il1]{2}y disab[li]ed",
116 "temporarily.*lock",
117 "un-?usua[li].activity",
118 "unable.*deliver",
119 "unauthorized.*activit",
120 "unauthorized.device",
121 "undelivered message",
122 "unread.*doc",
123 "unusual.activity",
124 "(?:unrecognized|Unusual|suspicious|unknown) (?:log|sign).?[io]n attempt",
125 "upgrade.*account",
126 "upgrade.notice",
127 "urgent message",
128 "urgent.verification",
129 "v[il1]o[li1]at[il1]on security",
130 "va[il1]{1}date.*ma[il1]{2}[ -]?box",
131 "verification ?-?require",
132 "verification( )?-?need",
133 "verify.your?.account",
134 "web ?-?ma[il1]{2}",
135 "web[ -]?ma[il1]{2}",
136 "will.be.suspended",
137 "your (customer )?account .as",
138 "your.office.365",
139 "your.online.access",
140 "de.activation",
141 "attn_task",
142 // https://github.com/sublime-security/static-files/blob/main/suspicious_subjects.txt
143 "account has been limited",
144 "action required",
145 "almost full",
146 "apd notifi cation",
147 "are you at your desk",
148 "are you available",
149 "attached file to docusign",
150 "banking is temporarily unavailable",
151 "bankofamerica",
152 "closing statement invoice",
153 "completed: docusign",
154 "de-activation of",
155 "delivery attempt",
156 "delivery stopped for shipment",
157 "detected suspicious",
158 "detected suspicious actvity",
159 "docu sign",
160 "document for you",
161 "document has been sent to you via docusign",
162 "document is ready for signature",
163 "docusign",
164 "encrypted message",
165 "failed delivery",
166 "fedex tracking",
167 "file was shared",
168 "freefax",
169 "fwd: due invoice paid",
170 "has shared",
171 "inbox is full",
172 "invitation to comment",
173 "invitation to edit",
174 "invoice due",
175 "left you a message",
176 "message from",
177 "new message",
178 "new voicemail",
179 "on desk",
180 "out of space",
181 "password reset",
182 "payment status",
183 "pay notification",
184 "quick reply",
185 "re: w-2",
186 "required",
187 "required: completed docusign",
188 "remittance",
189 "ringcentral",
190 "scanned image",
191 "secured files",
192 "secured pdf",
193 "security alert",
194 "new sign-in",
195 "new sign in",
196 "sign-in attempt",
197 "sign in attempt",
198 "staff review",
199 "suspicious activity",
200 "unrecognized login attempt",
201 "unusual signin",
202 "upgrade immediately",
203 "urgent",
204 "wants to share",
205 "w2",
206 "you have notifications pending",
207 "your account",
208 "your amazon order",
209 "your document settlement",
210 "your order with amazon",
211 "your password has been compromised",
212 )
213 or (
214 regex.icontains(subject.subject, 'account.has.been')
215 and not regex.icontains(subject.subject, 'account.has.been.*created')
216 )
217 or (
218 regex.icontains(sender.display_name,
219 "Admin",
220 "Administrator",
221 "Alert",
222 "Assistant",
223 "Authenticat(or|ion)",
224 "Billing",
225 "Benefits",
226 "Bonus",
227 "CEO",
228 "CFO",
229 "CIO",
230 "CTO",
231 "Chairman",
232 "Claim",
233 "Confirm",
234 "Cpanel Mail",
235 "Critical",
236 "Customer Service",
237 "Deal",
238 "Discount",
239 "Director",
240 "Exclusive",
241 "Executive",
242 "Fax",
243 "Free",
244 "Gift",
245 '\bHR\b',
246 "Helpdesk",
247 "Human Resources",
248 "Immediate",
249 "Important",
250 "Info",
251 "Information",
252 "Invoice",
253 '\bIT\b',
254 '\bLegal\b',
255 "Lottery",
256 "Management",
257 "Manager",
258 "Member Services",
259 "Notification",
260 "Offer",
261 "Official Communication",
262 "Operations",
263 "Order",
264 "Partner",
265 "Payment",
266 "Payroll",
267 "Postmaster",
268 "President",
269 "Premium",
270 "Prize",
271 "Receipt",
272 "Refund",
273 "Registrar",
274 "Required",
275 "Reward",
276 "Sales",
277 "Secretary",
278 "Security",
279 "Server",
280 "Service",
281 "Storage",
282 "Support",
283 "Sweepstakes",
284 "System",
285 "Tax",
286 "Tech Support",
287 "Update",
288 "Upgrade",
289 "Urgent",
290 "Validate",
291 "Verify",
292 "VIP",
293 "Webmaster",
294 "Winner",
295 "DocReq\\b"
296 )
297 // add negation for common FPs in the sender display_name
298 and not strings.icontains(sender.display_name, "service bulletin")
299 and not strings.icontains(sender.display_name, "automotive service")
300 )
301 )
302 and (
303 4 of (
304 any(recipients.to,
305 .email.domain.valid
306 and (
307 strings.icontains(body.current_thread.text, .email.email)
308 or strings.icontains(body.current_thread.text, .email.local_part)
309 )
310 ),
311 any(ml.nlu_classifier(body.current_thread.text).intents,
312 .name == "cred_theft" and .confidence in ("medium", "high")
313 ),
314 any(ml.nlu_classifier(body.current_thread.text).entities,
315 .name == "request"
316 ),
317 (
318 any(body.current_thread.links,
319 strings.iends_with(.display_text, ".pdf")
320 and strings.istarts_with(.display_text, "view")
321 )
322 ),
323 // recipient email address base64 encoded in link
324 any(body.links,
325 any(strings.scan_base64(.href_url.url,
326 ignore_padding=true,
327 format="url"
328 ),
329 any(recipients.to, strings.icontains(.., .email.email))
330 )
331 or strings.decode_hex(.href_url.fragment) == recipients.to[0].email.email
332 ),
333 (
334 // freemail providers should never be sending this type of email
335 sender.email.domain.domain in $free_email_providers
336
337 // if not freemail, it's suspicious if the sender's root domain
338 // doesn't match any links in the body
339 or all(body.links,
340 .href_url.domain.root_domain != sender.email.domain.root_domain
341 and (
342 .href_url.domain.root_domain not in $org_domains
343 // ignore recipient email addresses in the body in relation to this check
344 or (
345 .href_url.domain.root_domain in $org_domains
346 and any(recipients.to,
347 strings.icount(body.current_thread.text, .email.email) == strings.icount(body.current_thread.text,
348 .email.domain.domain
349 )
350 )
351 )
352 )
353 )
354
355 // bulk mailers should also never be sending this type of email
356 or all(filter(body.links,
357 .href_url.domain.domain not in (
358 "aka.ms",
359 "mimecast.com",
360 "mimecastprotect.com",
361 "cisco.com"
362 )
363 ),
364 .href_url.domain.root_domain in $bulk_mailer_url_root_domains
365 )
366 ),
367 // in case it's embedded in an image attachment
368 // note: don't use message_screenshot() because it's not limited to current_thread
369 // and may FP
370 any(attachments,
371 .file_type in $file_types_images
372 and any(file.explode(.),
373 any(ml.nlu_classifier(.scan.ocr.raw).intents,
374 .name == "cred_theft" and .confidence == "high"
375 )
376 )
377 ),
378 strings.contains(body.current_thread.text,
379 "Your mailbox can no longer send or receive messages."
380 ),
381 strings.contains(body.current_thread.text,
382 "this invoice has been overlooked"
383 ),
384 any(body.links,
385 strings.icontains(.href_url.query_params, 'redirect')
386 or any(.href_url.rewrite.encoders,
387 strings.icontains(., "open_redirect")
388 )
389 ),
390 // multiple entities displaying urgency
391 length(filter(ml.nlu_classifier(body.current_thread.text).entities,
392 .name == "urgency"
393 )
394 ) >= 2
395 // and any body links
396 and any(body.links,
397 // display text contains a request
398 any(ml.nlu_classifier(.display_text).entities, .name == "request")
399 ),
400 (
401 any(body.links,
402 .href_url.domain.root_domain in $self_service_creation_platform_domains
403 )
404 and any(ml.nlu_classifier(body.current_thread.text).tags,
405 .name == "invoice" and .confidence == "high"
406 )
407 ),
408 any(body.links,
409 // display text contains a request
410 (
411 any(ml.nlu_classifier(.display_text).entities, .name == "request")
412 or regex.match(.display_text, '^[^a-z]+$')
413 )
414 and (
415 .href_url.domain.domain in $url_shorteners
416 or .href_url.domain.domain in $social_landing_hosts
417 or .href_url.domain.root_domain in $url_shorteners
418 or .href_url.domain.root_domain in $social_landing_hosts
419 or .href_url.domain.domain in $free_file_hosts
420 or (
421 .href_url.domain.root_domain in (
422 "mimecast.com",
423 "mimecastprotect.com"
424 )
425 and any(.href_url.query_params_decoded['domain'],
426 strings.parse_url(strings.concat("https://", .)).domain.domain in $url_shorteners
427 or strings.parse_url(strings.concat("https://", .)).domain.root_domain in $url_shorteners
428 or strings.parse_url(strings.concat("https://", .)).domain.domain in $free_file_hosts
429 or strings.parse_url(strings.concat("https://", .)).domain.root_domain in $free_subdomain_hosts
430 or strings.parse_url(strings.concat("https://", .)).domain.domain in $social_landing_hosts
431 or strings.parse_url(strings.concat("https://", .)).domain.root_domain in $social_landing_hosts
432 )
433 )
434 )
435 ),
436 // common greetings via email.local_part
437 any(recipients.to,
438 length(.email.local_part) > 2
439 and
440 // use count to ensure the email address is not part of a disclaimer
441 strings.icount(body.current_thread.text, .email.local_part) >
442 // sum allows us to add more logic as needed
443 strings.icount(body.current_thread.text,
444 strings.concat('was sent to ', .email.email)
445 ) + strings.icount(body.current_thread.text,
446 strings.concat('intended for ', .email.email)
447 )
448 )
449 )
450 or (
451 (
452 // recipient's email address is in the body
453 any(recipients.to,
454 // use count to ensure the email address is not part of a disclaimer
455 strings.icount(body.current_thread.text, .email.email) >
456 // sum allows us to add more logic as needed
457 sum([
458 strings.icount(body.current_thread.text,
459 strings.concat('was sent to ', .email.email)
460 ),
461 strings.icount(body.current_thread.text,
462 strings.concat('intended for ', .email.email)
463 )
464 ]
465 )
466 )
467 // suspicious display text
468 or (
469 length(body.links) == 1
470 and all(body.links,
471 strings.ilike(.display_text, "*click here*", "*password*")
472 )
473 )
474 )
475 // link leads to a suspicious TLD or contains an IP address or contains multiple redirects
476 and any(body.links,
477 (
478 ml.link_analysis(., mode="aggressive").effective_url.domain.tld in $suspicious_tlds
479 or length(distinct(map(ml.link_analysis(., mode="aggressive").redirect_history,
480 .domain.root_domain
481 )
482 )
483 ) >= 4
484 or (
485 any(body.ips,
486 any(body.links, strings.icontains(.href_url.url, ..ip))
487 )
488 )
489 )
490 )
491 )
492 )
493 // exclude Google shared calendar messages
494 // Subject: "<sender name> has shared a calendar with you"
495 and headers.return_path.domain.domain != "calendar-server.bounces.google.com"
496 // negate calendar invites
497 and not (
498 0 < length(attachments) < 3
499 and all(attachments, .content_type in ("text/calendar", "application/ics"))
500 )
501 // negate replies
502 and (
503 (
504 (length(headers.references) > 0 or headers.in_reply_to is null)
505 and not (
506 (
507 strings.istarts_with(subject.subject, "RE:")
508 or strings.istarts_with(subject.subject, "R:")
509 or strings.istarts_with(subject.subject, "ODG:")
510 or strings.istarts_with(subject.subject, "答复:")
511 or strings.istarts_with(subject.subject, "AW:")
512 or strings.istarts_with(subject.subject, "TR:")
513 or strings.istarts_with(subject.subject, "FWD:")
514 or regex.icontains(subject.subject,
515 '^(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:'
516 )
517 )
518 )
519 )
520 or length(headers.references) == 0
521 )
522 // bounce-back and DMARC report negations
523 and not (
524 strings.like(sender.email.local_part,
525 "*postmaster*",
526 "*mailer-daemon*",
527 "*administrator*"
528 )
529 and (
530 any(attachments,
531 .content_type in (
532 "message/rfc822",
533 "message/delivery-status",
534 "text/calendar"
535 )
536 )
537 or (
538 length(attachments) == 1
539 and all(attachments, .content_type in ("application/gzip"))
540 and regex.icontains(subject.subject,
541 '(?:(Report\sDomain).*(Submitter).*(Report-ID))'
542 )
543 )
544 )
545 )
546 and (
547 (
548 profile.by_sender().prevalence != "common"
549 and not profile.by_sender_email().solicited
550 )
551 or (
552 profile.by_sender().any_messages_malicious_or_spam
553 and not profile.by_sender().any_messages_benign
554 )
555 )
556 // negate highly trusted sender domains unless they fail DMARC authentication
557 and not (
558 sender.email.domain.root_domain in $high_trust_sender_root_domains
559 and coalesce(headers.auth_summary.dmarc.pass, false)
560 )
561 // FP avoidance
562 and not any(ml.nlu_classifier(body.current_thread.text).topics,
563 .name in (
564 "Advertising and Promotions",
565 "Political Mail",
566 "News and Current Events",
567 "Newsletters and Digests"
568 )
569 and .confidence == "high"
570 )
571attack_types:
572 - "Credential Phishing"
573tactics_and_techniques:
574 - "Free email provider"
575 - "Social engineering"
576detection_methods:
577 - "Content analysis"
578 - "Header analysis"
579 - "Natural Language Understanding"
580 - "Sender analysis"
581 - "URL analysis"
582id: "c2bc8ca2-d207-5c7d-96e4-a0d3d33b2af5"