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 (0 < length(body.links) <= 15 or 0 < length(attachments) <= 3)
11 // the subject or display_name need some keywords which are voicemail related
12 and (
13 any([subject.subject, sender.display_name],
14 regex.icontains(.,
15 // split phrases that occur within 3 words between or only punctuation between them
16 '(?:v[nm](\b|[[:punct:]])?|\bvoice(?:mail|message)?|audio|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)',
17 // split phrases that start with "caller" that occur within 3 words between or only punctation
18 'ca[li1][li1](?:er)?(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:v[nm](\b|[[:punct:]])?|\bvoice(?:mail|message)?|audio|missed(?:\sa\s)?|left( a)?)',
19 // strong phrases
20 '(?:open mp3|audio 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)',
21 // starts in the format of `(4)` and contains some voicemail keywords
22 '^\(\d\)\s(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:message|voip|voice|unread|call)',
23 'ca[li1][li1](?:er)?(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:playback|transcript)',
24
25 // obfuscated phone number with at least one digit in the area code and at least one obfuscated number in the last group
26 // 555-555-555X, 555-555-XXXX, 555-5XX-XXXX
27 '\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]',
28 // obfuscated phone number with at least one digit in the prefix
29 // XXX-555-5555, XXX-5XX-XXXX
30 '\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',
31 )
32 )
33 // body.current_thread.text inspection should be very specific to avoid FP
34 or regex.icontains(
35 strings.replace_confusables(body.current_thread.text),
36 //body.current_thread.text,
37 'you (?:have |received )*(?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}\bvoice\s?(?:mail|audio|message)',
38 'sent (?:from|by) (?:your )?voice (?:mail )?system',
39 'new (?:voice(?:mail)?|audio) (?:message|notification|record)',
40 'voicemail (is )?attached',
41 'an? (?:new )?encrypted voicemail',
42 'a (?:new )?pending message',
43 'Your? have (?: an?)?incoming voiceRec',
44 "you(?:\'ve| have) a (?:new )?missed ca[li1][li1]",
45 'New Voicemail Received',
46 'left you a (?:\w+(\s\w+)?|[[:punct:]]+|\s+){0,3}(?:voice(?:mail)?|audio)(?: message)?',
47 'New missed ca[li1][li1] record',
48 'voicemail transcript(?:ion)?',
49 'Listen to VoiceMail',
50 'New voicemail from'
51 )
52 // phishing template observed https://platform.sublime.security/messages/341eed2be003036cdd3eeee575202df8a7472b6567d0dfa0f99c3b3fb42a8e7f
53 or strings.icontains(body.html.raw, '<title>Voicemail Notification</title>')
54 or strings.icontains(body.html.raw, '<!-- Voicemail phone logo')
55 )
56
57 and 2 of (
58 (
59 // the sender is a freemail
60 sender.email.domain.root_domain in $free_email_providers
61 ),
62 (
63 any(ml.nlu_classifier(body.current_thread.text).intents,
64 .name in ("cred_theft") and .confidence in ("medium", "high")
65 )
66 ),
67 (
68 any(attachments,
69 .content_type in ("html", "text", "text/html")
70 and any(ml.logo_detect(file.html_screenshot(.)).brands,
71 .name in ("Microsoft") and .confidence in ("medium", "high")
72 )
73 )
74 ),
75 (
76 regex.icontains(sender.display_name,
77 '(voice|audio|call|missed|caii)(\s?|-)(mail|message|recording|call|caii)|(transcription|Caller.?ID)'
78 )
79 ),
80 // attachment names are often HTML and voice mail related
81 (
82 any(attachments,
83 (
84 .content_type in ("html", "text", "text/html")
85 or .file_type in ("html", "unknown")
86 or .file_type == "pdf"
87 )
88 and (
89 regex.icontains(.file_name,
90 '(?:voice|audio|call|missed|caii|mail|message|recording|call|caii|transcription|v[nm]|audio|play|listen|unheard|msg)',
91 // contains a time
92 // 01min , 60secs
93 '0?[1-9]\s*min(?:(?:ute)?s)?',
94 '\d{1,2}\s*s(?:ec(?:ond)?s)?',
95 // (00:50s)
96 // 3:26 seconds
97 '[\(\[]?(?:\d{1,2}[\:\s-])\d{1,2}[\)\]]?\s*(?:s(?:(?:ecs?)onds)?)[\)\]]?',
98 // 03min25secs
99 '0?[1-9]\s*min(?:(?:ute)?s)?\d{1,2}\s*s(?:ec(?:ond)?s)?',
100 // [0:39]
101 // (0:39)
102 '[\(\[](?:\d{1,2}[\:\s-])\d{1,2}[\)\]]\s',
103 // contains an emoji
104 '[\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}]'
105 )
106 // somtimes there is no name, it's just the extension which is also strange
107 or .file_name in~ (".htm", ".html")
108 // or sometimes it has no name....
109 or .file_name is null
110 )
111 )
112 ),
113 // html attachment is small and contains javascript
114 (
115 any(attachments,
116 (
117 .content_type in ("html", "text", "text/html")
118 or .file_type in ("html", "unknown")
119 )
120 and (.size < 1500 and any(file.explode(.), length(.scan.html.scripts) > 0)
121 )
122 )
123 ),
124 (
125 any(attachments,
126 (
127 .content_type in ("html", "text", "text/html")
128 or .file_type in ("html", "unknown")
129 )
130 and any(recipients.to,
131 // the html attachment contains a receipient email address
132 strings.contains(file.parse_html(..).raw, .email.email)
133 // the sld of the domain is in the attachment name
134 or strings.contains(..file_name, .email.domain.sld)
135 )
136 )
137 ),
138 // the body links contain the recipients email
139 (
140 length(filter(recipients.to, .email.email != "" or .email.domain.valid)) > 0
141 and any(body.links,
142 any(recipients.to,
143 strings.icontains(..href_url.url, .email.email)
144 or strings.icontains(..href_url.url, .email.local_part)
145 )
146 )
147 ),
148 (
149 length(body.current_thread.text) < 700
150 and regex.icontains(body.current_thread.text,
151 'Méssãge|Méssage|Recéived|Addréss'
152 )
153 ),
154 (
155 // sender domain matches no body domains
156 // only inspect "links" that have a display_text and display_url is null to remove "plain text" email address from being caught
157 length(filter(body.links,
158 .display_text is not null and .display_url.url is null and .href_url.domain.valid
159 )
160 ) > 0
161 and all(filter(body.links,
162 .display_text is not null and .display_url.url is null and .href_url.domain.valid
163 ),
164 .href_url.domain.root_domain != sender.email.domain.root_domain
165 and .href_url.domain.root_domain not in $org_domains
166 and .href_url.domain.root_domain not in ("aka.ms")
167 and .href_url.domain.root_domain not in (
168 "unitelvoice.com",
169 "googleapis.com",
170 "dialmycalls.com",
171 "ringcentral.biz"
172 )
173 )
174 ),
175 // the body links contain vm related phrases
176 (
177 any(body.links,
178 regex.contains(.display_text, '[^a-z]*[A-Z][^a-z]*')
179 and regex.icontains(.display_text,
180 '(v[nm]|voice|audio|call|missed|caii)(\s?|-)(mail|message|recording|call|caii)|transcription|open mp3|audio note|listen|playback|\(?(?:\*\*\*|[0-9]{3})?.(?:\*\*\*|[0-9]{3})[^a-z]{0,2}(?:\*{4}|\d+\*+)|play'
181 )
182 // negate FP terms in link display texts
183 and not strings.icontains(.display_text, 'voice call center')
184 )
185 ),
186 (
187 any(body.links,
188 .href_url.path == "/ctt"
189 and regex.icontains(.display_text,
190 '(v[nm]|voice|audio|call|missed|caii)(\s?|-)(mail|message|recording|call|caii)|transcription|open mp3|audio note|listen|playback|\(?(?:\*\*\*|[0-9]{3})?.(?:\*\*\*|[0-9]{3})[^a-z]{0,2}(?:\*{4}|\d+\*+)|play'
191 )
192 // negate FP terms in link display texts
193 and not strings.icontains(.display_text, 'voice call center')
194 )
195 ),
196 // new domains
197 (
198 any(body.links,
199 network.whois(.href_url.domain).days_old < 10
200 and not strings.icontains(.href_url.path, "unsubscribe")
201 )
202 ),
203 (
204 any(recipients.to,
205 // recipient's SLD is in the sender's display name
206 strings.icontains(sender.display_name, .email.domain.sld)
207 // recipient's SLD is in the sender's display name
208 or strings.icontains(subject.subject, .email.domain.sld)
209 )
210 ),
211 // often times the subject or sender display name will contain time references
212 (
213 any([sender.display_name, subject.subject, body.current_thread.text],
214 regex.icontains(.,
215 // 01min , 60secs
216 '0?[1-9]\s*min(?:(?:ute)?s)?\b',
217 '\d{1,2}\s*s(?:ec(?:ond)?s)?\b',
218 // (00:50s)
219 // 3:26 seconds
220 '[\(\[]?(?:\d{1,2}[\:\s-])\d{1,2}[\)\]]?\s*(?:s(?:(?:ecs?)onds)?)[\)\]]?',
221 // 03min25secs
222 '0?[1-9]\s*min(?:(?:ute)?s)?\d{1,2}\s*s(?:ec(?:ond)?s)?',
223 // [0:39]
224 // (0:39)
225 '[\(\[](?:\d{1,2}[\:\s-])\d{1,2}[\)\]]\s'
226 )
227 )
228 ),
229 // often times the subject or sender display name will contain dates
230 (
231 any([sender.display_name, subject.subject],
232 // days of week
233 any([
234 'monday',
235 'tuesday',
236 'wednesday',
237 'thursday',
238 'friday',
239 'saturday',
240 'sunday'
241 ],
242 strings.icontains(.., .)
243 )
244 // months
245 // may is problematic for words like "Mayor", "Maybe", "MayFlower", etc
246 or any([
247 "January",
248 "February",
249 "March",
250 "April",
251 "June",
252 "July",
253 "August",
254 "September",
255 "October",
256 "November",
257 "December"
258 ],
259 strings.icontains(.., .)
260 )
261 // use a regex for May
262 or regex.icontains(., '\bmay\b')
263 // common date formats
264 or regex.contains(.,
265 // YYYY-MM-DD or YY-MM-DD (ISO 8601 format)
266 '\d{2}(\d{2})?-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])',
267 // MM/DD/YYYY or MM/DD/YY (US format)
268 '(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{2}(\d{2})?',
269 // DD/MM/YYYY or DD/MM/YY (European format)
270 '(0[1-9]|[12]\d|3[01])/(0[1-9]|1[0-2])/\d{2}(\d{2})?',
271 // Month DD, YYYY or Month DD, YY (e.g., March 15, 2024 or March 15, 24)
272 '(January|February|March|April|May|June|July|August|September|October|November|December) (0[1-9]|[12]\d|3[01]), \d{2}(\d{2})?'
273 )
274 // common time formats
275 or regex.contains(.,
276 // Example: 23:45, 08:30
277 '([01]\d|2[0-3]):([0-5]\d)',
278 // Example: 23:45:59, 08:30:12
279 '([01]\d|2[0-3]):([0-5]\d):([0-5]\d)',
280 // Example: 08:30 AM, 12:45 pm
281 '(0[1-9]|1[0-2]):([0-5]\d)\s?([AaPp][Mm])',
282 // Example: 08:30 AM, 12:45 pm
283 '(0[1-9]|1[0-2]):([0-5]\d):([0-5]\d) ?([AaPp][Mm])'
284 )
285 )
286 ),
287 // there are often emoji in the sender display name
288 (
289 any([sender.display_name, subject.subject],
290 // contains an emoji
291 regex.contains(.,
292 '[\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}]'
293 )
294 // negate where the emoji occur in tags
295 and not regex.contains(.,
296 '^(?:\[[^\]]*\]\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}][^\]]*\]'
297 )
298 )
299 ),
300 // an attachment is a pdf, image, or document that contains a url
301 (
302 1 <= length(attachments) <= 2
303 and any(attachments,
304 (
305 .file_type in $file_types_images
306 or .file_type == "pdf"
307 or .file_extension in $file_extensions_macros
308 )
309 and any(file.explode(.),
310 .scan.qr.type == "url"
311 or strings.icontains(.scan.qr.data, 'http')
312 or any(recipients.to,
313 strings.icontains(..scan.qr.data, .email.local_part)
314 or strings.icontains(..scan.qr.data, .email.email)
315 )
316 )
317 )
318 )
319 )
320
321 // negating legit replies and legitimate audio file attachments and known voicemail senders
322 and not (
323 sender.email.domain.valid
324 and sender.email.domain.root_domain in (
325 "magicjack.com",
326 "unitelvoice.com",
327 "voipinterface.net",
328 "ringcentral.biz",
329 "verizonwireless.com",
330 "t-mobile.com",
331 "justcall.io"
332 )
333 )
334 and not any(attachments, strings.starts_with(.content_type, "audio"))
335 and not (
336 (
337 strings.istarts_with(subject.subject, "RE:")
338 // out of office auto-reply
339 // the NLU model will handle these better natively soon
340 or strings.istarts_with(subject.subject, "Automatic reply:")
341 )
342 and (
343 length(headers.references) > 0
344 or any(headers.hops, any(.fields, strings.ilike(.name, "In-Reply-To")))
345 )
346 )
347 // negate highly trusted sender domains unless they fail DMARC authentication
348 and (
349 (
350 sender.email.domain.root_domain in $high_trust_sender_root_domains
351 and not headers.auth_summary.dmarc.pass
352 )
353 or sender.email.domain.root_domain not in $high_trust_sender_root_domains
354 )
355 // bounce-back negations
356 and not any(attachments,
357 any(file.parse_eml(.).attachments,
358 .content_type == "message/delivery-status"
359 )
360 )
361 // bounce-back negations
362 and not (
363 any(attachments,
364 .content_type in ("message/delivery-status", "text/calendar")
365 )
366 )
367 // negate bouncebacks from proofpoint
368 and not (
369 sender.display_name == "Mail Delivery Subsystem"
370 and strings.ends_with(headers.message_id, "pphosted.com>")
371 and any(headers.hops,
372 .index == 0 and strings.contains(.received.server.raw, "pphosted.com")
373 )
374 and any(attachments, .content_type == "message/rfc822")
375 )
376 // sender profile
377 and (
378 (
379 profile.by_sender().prevalence not in ("common")
380 and not profile.by_sender().solicited
381 )
382 or (
383 profile.by_sender().any_messages_malicious_or_spam
384 and not profile.by_sender().any_false_positives
385 )
386 )
387attack_types:
388 - "Credential Phishing"
389tactics_and_techniques:
390 - "Social engineering"
391detection_methods:
392 - "Content analysis"
393 - "Natural Language Understanding"
394 - "Sender analysis"
395 - "URL analysis"
396id: "74ba7787-e543-5ce8-b6eb-e1ecdb8f1d67"