Fake voicemail notification (untrusted sender)

This rule detects a common credential phishing vector enticing the user to engage with links under the premise that they have a voicemail to retrieve. The rule looks for voicemail verbiage in the display name, body, subject or a combination of those elements with emojis or a medium to high credential theft NLU Intent from first-time + unsolicited sender.

Sublime rule (View on GitHub)

  1name: "Fake voicemail notification (untrusted sender)"
  2description: |
  3  This rule detects a common credential phishing vector enticing the user to engage with links under the premise that they have a voicemail to retrieve.
  4  The rule looks for voicemail verbiage in the display name, body, subject or a combination of those elements with emojis or a medium to high credential theft NLU Intent from first-time + unsolicited sender.  
  5type: "rule"
  6severity: "medium"
  7source: |
  8  type.inbound
  9  // contains links or attachments
 10  and (
 11    0 < length(body.links) <= 25 or 0 < length(distinct(attachments, .md5)) <= 3
 12  )
 13  
 14  // the subject or display_name need some keywords which are voicemail related
 15  and (
 16    any([subject.subject, sender.display_name],
 17        regex.icontains(.,
 18                        // split phrases that occur within 3 words between or only punctuation between them
 19                        '(?:v[nm](\b|[[:punct:]])?|\bvoice(?:mail|message)?|audi[o0]|incoming|missed(?:\sa\s)?|left( a)?|wireless)(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:mail|message|msg|recording|received|notif|support|ca[li1][li1]\d*\b|ca[il1][il1](?:er)?|log|transcript(?:ion)?\b)',
 20                        // regex specific to v-mail, v_msg, v,mail, etc
 21                        // list of "secondary" words synced with regex above this one
 22                        'v[[:punct:]](?:mail|message|msg|recording|received|notif|support|ca[li1][li1]\d*\b|ca[il1][il1](?:er)?|log|transcript(?:ion)?\b)',
 23                        // split phrases that start with "caller" that occur within 3 words between or only punctation 
 24                        'ca[li1][li1](?:er)?(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:v[nm](\b|[[:punct:]])?|\bvoice(?:mail|message)?|audi[o0]|missed(?:\sa\s)?|left( a)?)',
 25                        // strong phrases
 26                        '(?:open mp3|audi[o0] note|\.wav|left a vm|[^\s]+voip[^\s]*|unanswered.*ca[li1][li1]|incoming.vm|left msg|wireless ca[li1][li1]er|VM Service|voice message|missed.ca[li1][li1](?:e[rd])?|\bca[li1][li1].(?:support|service)(?: for| log)?|missed.{0,10} VM|new voicemail from|new.v.m.from.\+?\d+|new voicemail?(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}transcript(s|ion)?|message received|incoming transmission)',
 27                        // starts in the format of `(4)` and contains some voicemail keywords
 28                        '^\(\d\)\s(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:message|voip|voice|unread|call)',
 29                        'ca[li1][li1](?:er)?(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:playback|transcript)',
 30  
 31                        // obfuscated phone number with at least one digit in the area code and at least one obfuscated number in the last group
 32                        // 555-555-555X, 555-555-XXXX, 555-5XX-XXXX
 33                        '\b1?\(?(\d{3}|\d{2}[\*X]|\d[\*X]{2})\)?[^a-z0-9]{0,2}(\d{2,3}|\d{2}[\*X]|\d[\*X]{2}|[\*X]{2,3})[^a-z0-9]{0,4}(\d{3}[\*X]|\d{2}[\*X]{2}|\d[\*X]{3}|[\*X]{3,4})[^0-9]',
 34                        // obfuscated phone number with at least one digit in the prefix
 35                        // XXX-555-5555, XXX-5XX-XXXX
 36                        '\b1?\(?(\d{2}[\*X]|\d[\*X]{2}|[\*X]{2,3})\)?[^a-z0-9]{0,2}(\d{2,3}|\d{2}[\*X]|\d[\*X]{2})[^a-z0-9]{0,4}(\d{4}|\d{3}[\*X]|\d{2}[\*X]{2}|\d[\*X]{3}|[\*X]{3,4})\b',
 37        )
 38    )
 39    // body.current_thread.text inspection should be very specific to avoid FP
 40    or regex.icontains(strings.replace_confusables(body.current_thread.text),
 41                       // body.current_thread.text,
 42                       'sent (?:from|by) (?:your )?voice (?:mail )?system',
 43                       'new (?:voice(?:mail)?|audi[o0]) (?:message|notification|record)',
 44                       'voicemail (is )?attached',
 45                       'an? (?:new )?encrypted voicemail',
 46                       'a (?:new )?pending message',
 47                       'Your? have (?: an?)?incoming voiceRec',
 48                       "you(?:\'ve| have) a (?:new )?missed ca[li1][li1]",
 49                       'New Voicemail Received',
 50                       'New missed ca[li1][li1] record',
 51                       'voicemail transcript(?:ion)?',
 52                       'Listen to VoiceMail',
 53                       'New voicemail from'
 54    )
 55    // pull out two regexes that could benefit from negations
 56    or (
 57      regex.icontains(body.current_thread.text,
 58                      // body.current_thread.text,
 59                      'you (?:have |received )*(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}\bvoice\s?(?:mail|audi[o0]|message)',
 60                      'left you a (?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:voice(?:mail)?|audi[o0])(?: message)?',
 61      )
 62      and not regex.icontains(body.current_thread.text,
 63                              '(?:I(?:\sjust)?|just(?: called you at (?:\d+[[:punct:]])+) and)? left you a (?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:voice(?:mail)?|audio)(?: message)?'
 64      )
 65      and not regex.icontains(body.current_thread.text,
 66                              'you (?:have |received )my voice\s?(?:mail|audio|message)'
 67      )
 68    )
 69    // Reuse the body.current_thread.text logic against the OCR output of the message screenshot
 70    or (
 71      all(attachments, .file_type in $file_types_images)
 72      and any((filter(file.explode(beta.message_screenshot()), .depth == 0)),
 73              regex.icontains(.scan.ocr.raw,
 74                              // body.current_thread.text,
 75                              'sent (?:from|by) (?:your )?voice (?:mail )?system',
 76                              'new (?:voice(?:mail)?|audio) (?:message|notification|record)',
 77                              'voicemail (is )?attached',
 78                              'an? (?:new )?encrypted voicemail',
 79                              'a (?:new )?pending message',
 80                              'Your? have (?: an?)?incoming voiceRec',
 81                              "you(?:\'ve| have) a (?:new )?missed ca[li1][li1]",
 82                              'New Voicemail Received',
 83                              'New missed ca[li1][li1] record',
 84                              'voicemail transcript(?:ion)?',
 85                              'Listen to VoiceMail',
 86                              'New voicemail from'
 87              )
 88              or (
 89                regex.icontains(.scan.ocr.raw,
 90                                // body.current_thread.text,
 91                                'you (?:have |received )*(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}\bvoice\s?(?:mail|audi[o0]|message)',
 92                                'left you a (?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:voice(?:mail)?|audi[o0])(?: message)?',
 93                )
 94                and not regex.icontains(body.current_thread.text,
 95                                        '(?:I(?:\sjust)?|just) left you a (?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:voice(?:mail)?|audio)(?: message)?'
 96                )
 97                and not regex.icontains(body.current_thread.text,
 98                                        'you (?:have |received )my voice\s?(?:mail|audio|message)'
 99                )
100              )
101      )
102    )
103    // phishing template observed https://platform.sublime.security/messages/341eed2be003036cdd3eeee575202df8a7472b6567d0dfa0f99c3b3fb42a8e7f
104    or strings.icontains(body.html.raw, '<title>Voicemail Notification</title>')
105    or strings.icontains(body.html.raw, '<!-- Voicemail phone logo')
106  )
107  and 2 of (
108    (
109      // the sender is a freemail
110      sender.email.domain.root_domain in $free_email_providers
111    ),
112    (
113      any(ml.nlu_classifier(body.current_thread.text).intents,
114          .name in ("cred_theft") and .confidence in ("medium", "high")
115      )
116      or 
117      // use the OCR from the message screenshot
118      any(filter(file.explode(beta.message_screenshot()), .depth == 0),
119          any(ml.nlu_classifier(.scan.ocr.raw).intents,
120              .name in ("cred_theft") and .confidence in ("medium", "high")
121          )
122      )
123    ),
124    (
125      any(attachments,
126          .content_type in ("html", "text", "text/html")
127          and any(ml.logo_detect(file.html_screenshot(.)).brands,
128                  .name in ("Microsoft") and .confidence in ("medium", "high")
129          )
130      )
131    ),
132    (
133      regex.icontains(sender.display_name,
134                      '(voice|audi[o0]|call|missed|caii)(\s?|-)(mail|message|recording|call|caii)|(transcription|Caller.?ID)'
135      )
136    ),
137    // attachment names are often HTML and voice mail related
138    (
139      any(attachments,
140          // this logic is reused below for eml attachments
141          // ensure updates occur both places
142          (
143            .content_type in ("html", "text", "text/html")
144            or .file_type in ("html", "unknown")
145            or .file_type == "pdf"
146          )
147          and (
148            regex.icontains(.file_name,
149                            '(?:voice|aud[i1l][o0]|call|missed|caii|mail|message|recording|call|caii|transcr[il1]ption|v[nm]|audi[o0]|play|listen|unheard|msg)',
150                            // contains a time
151                            // 01min , 60secs
152                            '0?[1-9]\s*min(?:(?:ute)?s)?',
153                            '\d{1,2}\s*s(?:ec(?:ond)?s)?',
154                            // (00:50s)
155                            // 3:26 seconds
156                            '[\(\[]?(?:\d{1,2}[\:\s-])\d{1,2}[\)\]]?\s*(?:s(?:(?:ecs?)onds)?)[\)\]]?',
157                            // 03min25secs
158                            '0?[1-9]\s*min(?:(?:ute)?s)?\d{1,2}\s*s(?:ec(?:ond)?s)?',
159                            // [0:39] 
160                            // (0:39) 
161                            '[\(\[](?:\d{1,2}[\:\s-])\d{1,2}[\)\]]\s',
162                            // contains an emoji
163                            '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
164            )
165            // somtimes there is no name, it's just the extension which is also strange
166            or .file_name in~ (".htm", ".html")
167            // or sometimes it has no name....
168            or .file_name is null
169          )
170      )
171    ),
172    // html attachment is small and contains javascript
173    (
174      any(attachments,
175          (
176            .content_type in ("html", "text", "text/html")
177            or .file_type in ("html", "unknown")
178          )
179          and (
180            .size < 1500 and any(file.explode(.), length(.scan.html.scripts) > 0)
181          )
182      )
183    ),
184    (
185      any(attachments,
186          (
187            .content_type in ("html", "text", "text/html")
188            or .file_type in ("html", "unknown")
189          )
190          and any(recipients.to,
191                  // the html attachment contains a receipient email address
192                  strings.contains(file.parse_html(..).raw, .email.email)
193                  // the sld of the domain is in the attachment name
194                  or strings.contains(..file_name, .email.domain.sld)
195          )
196      )
197    ),
198    // eml attachments
199    (
200      any(filter(attachments, .content_type == "message/rfc822"),
201          // which contain attachments
202          // this is the same logic as above
203          any(file.parse_eml(.).attachments,
204              (
205                .content_type in ("html", "text", "text/html")
206                or .file_type in ("html", "unknown")
207                or .file_type == "pdf"
208              )
209              and (
210                regex.icontains(.file_name,
211                                '(?:voice|aud[il1][o0]|call|missed|caii|mail|message|recording|call|caii|transcr[il1]ption|v[nm]|audi[o0]|play|listen|unheard|msg)',
212                                // contains a time
213                                // 01min , 60secs
214                                '0?[1-9]\s*min(?:(?:ute)?s)?',
215                                '\d{1,2}\s*s(?:ec(?:ond)?s)?',
216                                // (00:50s)
217                                // 3:26 seconds
218                                '[\(\[]?(?:\d{1,2}[\:\s-])\d{1,2}[\)\]]?\s*(?:s(?:(?:ecs?)onds)?)[\)\]]?',
219                                // 03min25secs
220                                '0?[1-9]\s*min(?:(?:ute)?s)?\d{1,2}\s*s(?:ec(?:ond)?s)?',
221                                // [0:39] 
222                                // (0:39) 
223                                '[\(\[](?:\d{1,2}[\:\s-])\d{1,2}[\)\]]\s',
224                                // contains an emoji
225                                '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
226                )
227                // somtimes there is no name, it's just the extension which is also strange
228                or .file_name in~ (".htm", ".html")
229                // or sometimes it has no name....
230                or .file_name is null
231              )
232          )
233      )
234    ),
235    // attached eml sender/recipeient/subject are all the same as the outer
236    // and have an attachment or body links
237    (
238      any(filter(attachments, .content_type == "message/rfc822"),
239          // which contain attachments
240          // this is the same logic as above
241          file.parse_eml(.).subject.subject == subject.subject
242          and file.parse_eml(.).sender.email.email == sender.email.email
243          and (
244            length(file.parse_eml(.).recipients.to) == length(recipients.to)
245            and all(recipients.to,
246                    .email.email in map(file.parse_eml(..).recipients.to,
247                                        .email.email
248                    )
249            )
250          )
251          and (
252            // there are attachments
253            length(file.parse_eml(.).attachments) > 0
254            // or body links
255            or length(filter(file.parse_eml(.).body.links,
256                             .href_url.domain.domain not in $org_domains
257                             and .href_url.domain.root_domain not in $org_domains
258                      )
259            ) > 0
260          )
261      )
262    ),
263    // the body links contain the recipients email
264    (
265      length(filter(recipients.to, .email.email != "" or .email.domain.valid)) > 0
266      and any(body.links,
267              any(recipients.to,
268                  strings.icontains(..href_url.url, .email.email)
269                  or strings.icontains(..href_url.url, .email.local_part)
270              )
271      )
272    ),
273    (
274      length(body.current_thread.text) < 700
275      and regex.icontains(body.current_thread.text,
276                          'Méssãge|Méssage|Recéived|Addréss'
277      )
278    ),
279    (
280      // sender domain matches no body domains
281      // only inspect "links" that have a display_text and display_url is null to remove "plain text" email address from being caught
282      length(filter(body.links,
283                    .display_text is not null
284                    and .display_url.url is null
285                    and .href_url.domain.valid
286             )
287      ) > 0
288      and all(filter(body.links,
289                     .display_text is not null
290                     and .display_url.url is null
291                     and .href_url.domain.valid
292              ),
293              .href_url.domain.root_domain != sender.email.domain.root_domain
294              and .href_url.domain.root_domain not in $org_domains
295              and .href_url.domain.root_domain not in ("aka.ms")
296              and .href_url.domain.root_domain not in (
297                "unitelvoice.com",
298                "googleapis.com",
299                "dialmycalls.com",
300                "ringcentral.biz"
301              )
302      )
303    ),
304    // the body links contain vm related phrases
305    (
306      any(body.links,
307          regex.contains(.display_text, '[^a-z]*[A-Z][^a-z]*')
308          and regex.icontains(.display_text,
309                              '(v[nm]|voice|audi[o0]|call|missed|caii)(\s?|-)(mail|message|recording|call|caii)|transcription|open mp3|audi[o0] note|listen|playback|\(?(?:\*\*\*|[0-9]{3})?.(?:\*\*\*|[0-9]{3})[^a-z]{0,2}(?:\*{4}|\d+\*+)|play'
310          )
311          // negate FP terms in link display texts
312          and not strings.icontains(.display_text, 'voice call center')
313      )
314    ),
315    (
316      any(body.links,
317          .href_url.path == "/ctt"
318          and regex.icontains(.display_text,
319                              '(v[nm]|voice|audi[o0]|call|missed|caii)(\s?|-)(mail|message|recording|call|caii)|transcription|open mp3|audi[o0] note|listen|playback|\(?(?:\*\*\*|[0-9]{3})?.(?:\*\*\*|[0-9]{3})[^a-z]{0,2}(?:\*{4}|\d+\*+)|play'
320          )
321          // negate FP terms in link display texts
322          and not strings.icontains(.display_text, 'voice call center')
323      )
324    ),
325    // new domains
326    (
327      any(body.links,
328          network.whois(.href_url.domain).days_old < 10
329          and not strings.icontains(.href_url.path, "unsubscribe")
330      )
331    ),
332    // sld use in sender/subject selements
333    (
334      any(recipients.to,
335          // recipient's SLD is in the sender's display name
336          strings.icontains(sender.display_name, .email.domain.sld)
337          // recipient's SLD is in the sender's display name
338          or strings.icontains(subject.subject, .email.domain.sld)
339          // recipient's SLD is in the senders local_part
340          or strings.icontains(sender.email.local_part, .email.domain.sld)
341      )
342    ),
343    // often times the subject or sender display name will contain time references
344    (
345      any([sender.display_name, subject.subject, body.current_thread.text],
346          regex.icontains(.,
347                          // 01min , 60secs
348                          '0?[1-9]\s*min(?:(?:ute)?s)?\b',
349                          '\d{1,2}\s*s(?:ec(?:ond)?s)?\b',
350                          // (00:50s)
351                          // 3:26 seconds
352                          '[\(\[]?(?:\d{1,2}[\:\s-])\d{1,2}[\)\]]?\s*(?:s(?:(?:ecs?)onds)?)[\)\]]?',
353                          // 03min25secs
354                          '0?[1-9]\s*min(?:(?:ute)?s)?\d{1,2}\s*s(?:ec(?:ond)?s)?',
355                          // [0:39] 
356                          // (0:39) 
357                          '[\(\[](?:\d{1,2}[\:\s-])\d{1,2}[\)\]]\s'
358          )
359      )
360      // resuse the same logic against ORC output of message_screenshot
361      or any(filter(file.explode(beta.message_screenshot()), .depth == 0),
362             regex.icontains(.scan.ocr.raw,
363                             // 01min , 60secs
364                             '0?[1-9]\s*min(?:(?:ute)?s)?\b',
365                             '\d{1,2}\s*s(?:ec(?:ond)?s)?\b',
366                             // (00:50s)
367                             // 3:26 seconds
368                             '[\(\[]?(?:\d{1,2}[\:\s-])\d{1,2}[\)\]]?\s*(?:s(?:(?:ecs?)onds)?)[\)\]]?',
369                             // 03min25secs
370                             '0?[1-9]\s*min(?:(?:ute)?s)?\d{1,2}\s*s(?:ec(?:ond)?s)?',
371                             // [0:39] 
372                             // (0:39) 
373                             '[\(\[](?:\d{1,2}[\:\s-])\d{1,2}[\)\]]\s'
374             )
375      )
376    ),
377    // often times the subject or sender display name will contain dates
378    (
379      any([sender.display_name, subject.subject],
380          // days of week
381          any([
382                'monday',
383                'tuesday',
384                'wednesday',
385                'thursday',
386                'friday',
387                'saturday',
388                'sunday'
389              ],
390              strings.icontains(.., .)
391          )
392          // months
393          // may is problematic for words like "Mayor", "Maybe", "MayFlower", etc
394          or any([
395                   "January",
396                   "February",
397                   "March",
398                   "April",
399                   "June",
400                   "July",
401                   "August",
402                   "September",
403                   "October",
404                   "November",
405                   "December"
406                 ],
407                 strings.icontains(.., .)
408          )
409          // use a regex for May
410          or regex.icontains(., '\bmay\b')
411          // common date formats
412          or regex.contains(.,
413                            // YYYY-MM-DD or YY-MM-DD (ISO 8601 format)
414                            '\d{2}(\d{2})?-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])',
415                            // MM/DD/YYYY or MM/DD/YY (US format)
416                            '(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{2}(\d{2})?',
417                            // DD/MM/YYYY or DD/MM/YY (European format)
418                            '(0[1-9]|[12]\d|3[01])/(0[1-9]|1[0-2])/\d{2}(\d{2})?',
419                            // Month DD, YYYY or Month DD, YY (e.g., March 15, 2024 or March 15, 24)
420                            '(January|February|March|April|May|June|July|August|September|October|November|December) (0[1-9]|[12]\d|3[01]), \d{2}(\d{2})?'
421          )
422          // common time formats
423          or regex.contains(.,
424                            // Example: 23:45, 08:30
425                            '([01]\d|2[0-3]):([0-5]\d)',
426                            // Example: 23:45:59, 08:30:12
427                            '([01]\d|2[0-3]):([0-5]\d):([0-5]\d)',
428                            // Example: 08:30 AM, 12:45 pm
429                            '(0[1-9]|1[0-2]):([0-5]\d)\s?([AaPp][Mm])',
430                            // Example: 08:30 AM, 12:45 pm
431                            '(0[1-9]|1[0-2]):([0-5]\d):([0-5]\d) ?([AaPp][Mm])'
432          )
433      )
434      // or use the OCR results from beta.message_screenshot
435      or any(filter(file.explode(beta.message_screenshot()), .depth == 0),
436             // days of week
437             any([
438                   'monday',
439                   'tuesday',
440                   'wednesday',
441                   'thursday',
442                   'friday',
443                   'saturday',
444                   'sunday'
445                 ],
446                 strings.icontains(..scan.ocr.raw, .)
447             )
448             // months
449             // may is problematic for words like "Mayor", "Maybe", "MayFlower", etc
450             or any([
451                      "January",
452                      "February",
453                      "March",
454                      "April",
455                      "June",
456                      "July",
457                      "August",
458                      "September",
459                      "October",
460                      "November",
461                      "December"
462                    ],
463                    strings.icontains(..scan.ocr.raw, .)
464             )
465             // use a regex for May
466             or regex.icontains(.scan.ocr.raw, '\bmay\b')
467             // common date formats
468             or regex.contains(.scan.ocr.raw,
469                               // YYYY-MM-DD or YY-MM-DD (ISO 8601 format)
470                               '\d{2}(\d{2})?-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])',
471                               // MM/DD/YYYY or MM/DD/YY (US format)
472                               '(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{2}(\d{2})?',
473                               // DD/MM/YYYY or DD/MM/YY (European format)
474                               '(0[1-9]|[12]\d|3[01])/(0[1-9]|1[0-2])/\d{2}(\d{2})?',
475                               // Month DD, YYYY or Month DD, YY (e.g., March 15, 2024 or March 15, 24)
476                               '(January|February|March|April|May|June|July|August|September|October|November|December) (0[1-9]|[12]\d|3[01]), \d{2}(\d{2})?'
477             )
478             // common time formats
479             or regex.contains(.scan.ocr.raw,
480                               // Example: 23:45, 08:30
481                               '([01]\d|2[0-3]):([0-5]\d)',
482                               // Example: 23:45:59, 08:30:12
483                               '([01]\d|2[0-3]):([0-5]\d):([0-5]\d)',
484                               // Example: 08:30 AM, 12:45 pm
485                               '(0[1-9]|1[0-2]):([0-5]\d)\s?([AaPp][Mm])',
486                               // Example: 08:30 AM, 12:45 pm
487                               '(0[1-9]|1[0-2]):([0-5]\d):([0-5]\d) ?([AaPp][Mm])'
488             )
489      )
490    ),
491    // there are often emoji in the sender display name
492    (
493      any([sender.display_name, subject.subject],
494          // contains an emoji
495          regex.contains(.,
496                         '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
497          )
498          // negate where the emoji occur in tags
499          and not regex.contains(.,
500                                 '^(?:\[[^\]]*\]\s*)*\[[^\]]*[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}][^\]]*\]'
501          )
502      )
503    ),
504    // an attachment is a pdf, image, or document that contains a url
505    (
506      1 <= length(attachments) <= 2
507      and any(attachments,
508              (
509                .file_type in $file_types_images
510                or .file_type == "pdf"
511                or .file_extension in $file_extensions_macros
512              )
513              and any(file.explode(.),
514                      .scan.qr.type == "url"
515                      or strings.icontains(.scan.qr.data, 'http')
516                      or any(recipients.to,
517                             strings.icontains(..scan.qr.data, .email.local_part)
518                             or strings.icontains(..scan.qr.data, .email.email)
519                      )
520              )
521      )
522    )
523  )
524  
525  // negating legit replies and legitimate audio file attachments and known voicemail senders
526  and not (
527    sender.email.domain.valid
528    and sender.email.domain.root_domain in (
529      "magicjack.com",
530      "unitelvoice.com",
531      "voipinterface.net",
532      "ringcentral.biz",
533      "verizonwireless.com",
534      "t-mobile.com",
535      "justcall.io"
536    )
537  )
538  and not any(attachments, strings.starts_with(.content_type, "audio"))
539  and not (
540    (
541      strings.istarts_with(subject.subject, "RE:")
542      // out of office auto-reply
543      // the NLU model will handle these better natively soon
544      or strings.istarts_with(subject.subject, "Automatic reply:")
545    )
546    and (
547      length(headers.references) > 0
548      or any(headers.hops, any(.fields, strings.ilike(.name, "In-Reply-To")))
549    )
550  )
551  // negate highly trusted sender domains unless they fail DMARC authentication
552  and (
553    (
554      sender.email.domain.root_domain in $high_trust_sender_root_domains
555      and not headers.auth_summary.dmarc.pass
556    )
557    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
558  )
559  // bounce-back negations
560  and not any(attachments,
561              any(file.parse_eml(.).attachments,
562                  .content_type == "message/delivery-status"
563              )
564  )
565  // bounce-back negations
566  and not (
567    any(attachments,
568        .content_type in ("message/delivery-status", "text/calendar")
569    )
570  )
571  // negate bouncebacks from proofpoint
572  and not (
573    sender.display_name == "Mail Delivery Subsystem"
574    and strings.ends_with(headers.message_id, "pphosted.com>")
575    and any(headers.hops,
576            .index == 0 and strings.contains(.received.server.raw, "pphosted.com")
577    )
578    and any(attachments, .content_type == "message/rfc822")
579  )
580  // an impersonated high trust domain 
581  and (
582    (
583      sender.email.domain.root_domain in $high_trust_sender_root_domains
584      and not headers.auth_summary.dmarc.pass
585    )
586  
587    // sender profile
588    or (
589      (
590        not sender.email.domain.root_domain in $org_domains
591        and (profile.by_sender().prevalence not in ("common"))
592        and not profile.by_sender().solicited
593      )
594      or (
595        profile.by_sender().any_messages_malicious_or_spam
596        and not profile.by_sender().any_false_positives
597      )
598    )
599  )  
600attack_types:
601  - "Credential Phishing"
602tactics_and_techniques:
603  - "Social engineering"
604detection_methods:
605  - "Content analysis"
606  - "Natural Language Understanding"
607  - "Sender analysis"
608  - "URL analysis"
609id: "74ba7787-e543-5ce8-b6eb-e1ecdb8f1d67"
to-top