Skip to content

Commit

Permalink
Sync from PR#2167
Browse files Browse the repository at this point in the history
Create PR2167_link_credential_phishing_voicemail_language.yml by @zoomequipd
#2167
Source SHA 327d92a
Triggered by @zoomequipd
  • Loading branch information
Sublime Rule Testing Bot committed Nov 25, 2024
1 parent cf02228 commit 506af7e
Showing 1 changed file with 19 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: "PR#2167 - Fake voicemail notification (untrusted sender)"
description: |
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.
type: "rule"
severity: "medium"
source: "type.inbound\n// contains links or attachments\nand (0 < length(body.links) <= 15 or 0 < length(attachments) <= 3)\n// the subject or display_name need some keywords which are voicemail related\nand (\n any([subject.subject, sender.display_name],\n regex.icontains(.,\n // split phrases that occur within 3 words between or only punctuation between them\n '(?: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)',\n // split phrases that start with \"caller\" that occur within 3 words between or only punctation \n 'ca[li1][li1](?:er)?(?:\\w+(\\s\\w+)?|[[:punct:]]+|\\s+){0,3}(?:v[nm](\\b|[[:punct:]])?|\\bvoice(?:mail|message)?|audio|missed(?:\\sa\\s)?|left( a)?)',\n // strong phrases\n '(?: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)',\n // starts in the format of `(4)` and contains some voicemail keywords\n '^\\(\\d\\)\\s(?:\\w+(\\s\\w+)?|[[:punct:]]+|\\s+){0,3}(?:message|voip|voice|unread|call)',\n 'ca[li1][li1](?:er)?(?:\\w+(\\s\\w+)?|[[:punct:]]+|\\s+){0,3}(?:playback|transcript)',\n\n // obfuscated phone number with at least one digit in the area code and at least one obfuscated number in the last group\n // 555-555-555X, 555-555-XXXX, 555-5XX-XXXX\n '\\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]',\n // obfuscated phone number with at least one digit in the prefix\n // XXX-555-5555, XXX-5XX-XXXX\n '\\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',\n )\n )\n // body.current_thread.text inspection should be very specific to avoid FP\n or regex.icontains(\n strings.replace_confusables(body.current_thread.text),\n //body.current_thread.text,\n 'you (?:have |received )*(?:\\w+(\\s\\w+)?|[[:punct:]]+|\\s+){0,3}\\bvoice\\s?(?:mail|audio|message)',\n 'sent (?:from|by) (?:your )?voice (?:mail )?system',\n 'new (?:voice(?:mail)?|audio) (?:message|notification|record)',\n 'voicemail (is )?attached',\n 'an? (?:new )?encrypted voicemail',\n 'a (?:new )?pending message',\n 'Your? have (?: an?)?incoming voiceRec',\n \"you(?:\\'ve| have) a (?:new )?missed ca[li1][li1]\",\n 'New Voicemail Received',\n 'left you a (?:\\w+(\\s\\w+)?|[[:punct:]]+|\\s+){0,3}(?:voice(?:mail)?|audio)(?: message)?',\n 'New missed ca[li1][li1] record',\n 'voicemail transcript(?:ion)?',\n 'Listen to VoiceMail'\n )\n // phishing template observed https://platform.sublime.security/messages/341eed2be003036cdd3eeee575202df8a7472b6567d0dfa0f99c3b3fb42a8e7f\n or strings.icontains(body.html.raw, '<title>Voicemail Notification</title>')\n or strings.icontains(body.html.raw, '<!-- Voicemail phone logo')\n)\n\nand 2 of (\n (\n // the sender is a freemail\n sender.email.domain.root_domain in $free_email_providers\n ),\n (\n any(ml.nlu_classifier(body.current_thread.text).intents,\n .name in (\"cred_theft\") and .confidence in (\"medium\", \"high\")\n )\n ),\n (\n any(attachments,\n .content_type in (\"html\", \"text\", \"text/html\")\n and any(ml.logo_detect(file.html_screenshot(.)).brands,\n .name in (\"Microsoft\") and .confidence in (\"medium\", \"high\")\n )\n )\n ),\n (\n regex.icontains(sender.display_name,\n '(voice|audio|call|missed|caii)(\\s?|-)(mail|message|recording|call|caii)|(transcription|Caller.?ID)'\n )\n ),\n // attachment names are often HTML and voice mail related\n (\n any(attachments,\n (\n .content_type in (\"html\", \"text\", \"text/html\")\n or .file_type in (\"html\", \"unknown\")\n or .file_type == \"pdf\"\n )\n and (\n regex.icontains(.file_name,\n '(?:voice|audio|call|missed|caii|mail|message|recording|call|caii|transcription|v[nm]|audio|play|listen|unheard|msg)',\n // contains a time\n // 01min , 60secs\n '0?[1-9]\\s*min(?:(?:ute)?s)?',\n '\\d{1,2}\\s*s(?:ec(?:ond)?s)?',\n // (00:50s)\n // 3:26 seconds\n '[\\(\\[]?(?:\\d{1,2}[\\:\\s-])\\d{1,2}[\\)\\]]?\\s*(?:s(?:(?:ecs?)onds)?)[\\)\\]]?',\n // 03min25secs\n '0?[1-9]\\s*min(?:(?:ute)?s)?\\d{1,2}\\s*s(?:ec(?:ond)?s)?',\n // [0:39] \n // (0:39) \n '[\\(\\[](?:\\d{1,2}[\\:\\s-])\\d{1,2}[\\)\\]]\\s',\n // contains an emoji\n '[\\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}]'\n )\n // somtimes there is no name, it's just the extension which is also strange\n or .file_name in~ (\".htm\", \".html\")\n // or sometimes it has no name....\n or .file_name is null\n )\n )\n ),\n // html attachment is small and contains javascript\n (\n any(attachments,\n (\n .content_type in (\"html\", \"text\", \"text/html\")\n or .file_type in (\"html\", \"unknown\")\n )\n and (.size < 1500 and any(file.explode(.), length(.scan.html.scripts) > 0)\n )\n )\n ),\n (\n any(attachments,\n (\n .content_type in (\"html\", \"text\", \"text/html\")\n or .file_type in (\"html\", \"unknown\")\n )\n and any(recipients.to,\n // the html attachment contains a receipient email address\n strings.contains(file.parse_html(..).raw, .email.email)\n // the sld of the domain is in the attachment name\n or strings.contains(..file_name, .email.domain.sld)\n )\n )\n ),\n // the body links contain the recipients email\n (\n length(filter(recipients.to, .email.email != \"\" or .email.domain.valid)) > 0\n and any(body.links,\n any(recipients.to,\n strings.icontains(..href_url.url, .email.email)\n or strings.icontains(..href_url.url, .email.local_part)\n )\n )\n ),\n (\n length(body.current_thread.text) < 700\n and regex.icontains(body.current_thread.text,\n 'Méssãge|Méssage|Recéived|Addréss'\n )\n ),\n (\n // sender domain matches no body domains\n // only inspect \"links\" that have a display_text and display_url is null to remove \"plain text\" email address from being caught\n length(filter(body.links,\n .display_text is not null and .display_url.url is null and .href_url.domain.valid\n )\n ) > 0\n and all(filter(body.links,\n .display_text is not null and .display_url.url is null and .href_url.domain.valid\n ),\n .href_url.domain.root_domain != sender.email.domain.root_domain\n and .href_url.domain.root_domain not in $org_domains\n and .href_url.domain.root_domain not in (\"aka.ms\")\n and .href_url.domain.root_domain not in (\n \"unitelvoice.com\",\n \"googleapis.com\",\n \"dialmycalls.com\",\n \"ringcentral.biz\"\n )\n )\n ),\n // the body links contain vm related phrases\n (\n any(body.links,\n regex.contains(.display_text, '[^a-z]*[A-Z][^a-z]*')\n and regex.icontains(.display_text,\n '(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'\n )\n // negate FP terms in link display texts\n and not strings.icontains(.display_text, 'voice call center')\n )\n ),\n (\n any(body.links,\n .href_url.path == \"/ctt\"\n and regex.icontains(.display_text,\n '(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'\n )\n // negate FP terms in link display texts\n and not strings.icontains(.display_text, 'voice call center')\n )\n ),\n // new domains\n (\n any(body.links,\n network.whois(.href_url.domain).days_old < 10\n and not strings.icontains(.href_url.path, \"unsubscribe\")\n )\n ),\n (\n any(recipients.to,\n // recipient's SLD is in the sender's display name\n strings.icontains(sender.display_name, .email.domain.sld)\n // recipient's SLD is in the sender's display name\n or strings.icontains(subject.subject, .email.domain.sld)\n )\n ),\n // often times the subject or sender display name will contain time references\n (\n any([sender.display_name, subject.subject, body.current_thread.text],\n regex.icontains(.,\n // 01min , 60secs\n '0?[1-9]\\s*min(?:(?:ute)?s)?\\b',\n '\\d{1,2}\\s*s(?:ec(?:ond)?s)?\\b',\n // (00:50s)\n // 3:26 seconds\n '[\\(\\[]?(?:\\d{1,2}[\\:\\s-])\\d{1,2}[\\)\\]]?\\s*(?:s(?:(?:ecs?)onds)?)[\\)\\]]?',\n // 03min25secs\n '0?[1-9]\\s*min(?:(?:ute)?s)?\\d{1,2}\\s*s(?:ec(?:ond)?s)?',\n // [0:39] \n // (0:39) \n '[\\(\\[](?:\\d{1,2}[\\:\\s-])\\d{1,2}[\\)\\]]\\s'\n )\n )\n ),\n // often times the subject or sender display name will contain dates\n (\n any([sender.display_name, subject.subject],\n // days of week\n any([\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n 'sunday'\n ],\n strings.icontains(.., .)\n )\n // months\n // may is problematic for words like \"Mayor\", \"Maybe\", \"MayFlower\", etc\n or any([\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\"\n ],\n strings.icontains(.., .)\n )\n // use a regex for May\n or regex.icontains(., '\\bmay\\b')\n // common date formats\n or regex.contains(.,\n // YYYY-MM-DD or YY-MM-DD (ISO 8601 format)\n '\\d{2}(\\d{2})?-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])',\n // MM/DD/YYYY or MM/DD/YY (US format)\n '(0[1-9]|1[0-2])/(0[1-9]|[12]\\d|3[01])/\\d{2}(\\d{2})?',\n // DD/MM/YYYY or DD/MM/YY (European format)\n '(0[1-9]|[12]\\d|3[01])/(0[1-9]|1[0-2])/\\d{2}(\\d{2})?',\n // Month DD, YYYY or Month DD, YY (e.g., March 15, 2024 or March 15, 24)\n '(January|February|March|April|May|June|July|August|September|October|November|December) (0[1-9]|[12]\\d|3[01]), \\d{2}(\\d{2})?'\n )\n // common time formats\n or regex.contains(.,\n // Example: 23:45, 08:30\n '([01]\\d|2[0-3]):([0-5]\\d)',\n // Example: 23:45:59, 08:30:12\n '([01]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)',\n // Example: 08:30 AM, 12:45 pm\n '(0[1-9]|1[0-2]):([0-5]\\d)\\s?([AaPp][Mm])',\n // Example: 08:30 AM, 12:45 pm\n '(0[1-9]|1[0-2]):([0-5]\\d):([0-5]\\d) ?([AaPp][Mm])'\n )\n )\n ),\n // there are often emoji in the sender display name\n (\n any([sender.display_name, subject.subject],\n // contains an emoji\n regex.contains(.,\n '[\\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}]'\n )\n // negate where the emoji occur in tags\n and not regex.contains(.,\n '^(?:\\[[^\\]]*\\]\\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}][^\\]]*\\]'\n )\n )\n ),\n // an attachment is a pdf or image that contains a url\n (\n 1 <= length(attachments) <= 2\n and any(attachments,\n (.file_type in $file_types_images or .file_type == \"pdf\")\n and any(file.explode(.),\n .scan.qr.type == \"url\"\n or strings.icontains(.scan.qr.data, 'http')\n or any(recipients.to,\n strings.icontains(..scan.qr.data, .email.local_part)\n or strings.icontains(..scan.qr.data, .email.email)\n )\n )\n )\n )\n)\n\n// negating legit replies and legitimate audio file attachments and known voicemail senders\nand not (\n sender.email.domain.valid\n and sender.email.domain.root_domain in (\n \"magicjack.com\",\n \"unitelvoice.com\",\n \"voipinterface.net\",\n \"ringcentral.biz\",\n \"verizonwireless.com\",\n \"t-mobile.com\",\n \"justcall.io\"\n )\n)\nand not any(attachments, strings.starts_with(.content_type, \"audio\"))\nand not (\n (\n strings.istarts_with(subject.subject, \"RE:\")\n // out of office auto-reply\n // the NLU model will handle these better natively soon\n or strings.istarts_with(subject.subject, \"Automatic reply:\")\n )\n and (\n length(headers.references) > 0\n or any(headers.hops, any(.fields, strings.ilike(.name, \"In-Reply-To\")))\n )\n)\n// negate highly trusted sender domains unless they fail DMARC authentication\nand (\n (\n sender.email.domain.root_domain in $high_trust_sender_root_domains\n and not headers.auth_summary.dmarc.pass\n )\n or sender.email.domain.root_domain not in $high_trust_sender_root_domains\n)\n// bounce-back negations\nand not any(attachments,\n any(file.parse_eml(.).attachments,\n .content_type == \"message/delivery-status\"\n )\n )\n// bounce-back negations\nand not (\n any(attachments,\n .content_type in (\"message/delivery-status\", \"text/calendar\")\n )\n)\n// negate bouncebacks from proofpoint\nand not (\n sender.display_name == \"Mail Delivery Subsystem\"\n and strings.ends_with(headers.message_id, \"pphosted.com>\")\n and any(headers.hops,\n .index == 0 and strings.contains(.received.server.raw, \"pphosted.com\")\n )\n and any(attachments, .content_type == \"message/rfc822\")\n)\n// sender profile\nand (\n (\n profile.by_sender().prevalence not in (\"common\") // this avoids hitting on legitmate voicemail \n or not profile.by_sender_email().solicited\n )\n or (\n profile.by_sender().any_messages_malicious_or_spam\n and not profile.by_sender().any_false_positives\n )\n)\n"
attack_types:
- "Credential Phishing"
tactics_and_techniques:
- "Social engineering"
detection_methods:
- "Content analysis"
- "Natural Language Understanding"
- "Sender analysis"
- "URL analysis"
id: "c5a1d510-05f7-4cd8-bc78-d70d0d204949"
testing_pr: 2167
testing_sha: 327d92a05c5b12afe6cbf52f399ea7ffc4c4dfa4

0 comments on commit 506af7e

Please sign in to comment.