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