Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create impersonation_capitalone.yml #2151

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions detection-rules/impersonation_capitalone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: "Brand Impersonation: Capital One"
description: "This detection rule identifies inbound messages containing Capital One branding indicators in display names, sender addresses, message content, or embedded logos, while excluding legitimate Capital One domains and authenticated communications from known trusted senders."
type: "rule"
severity: "high"
source: |
type.inbound
and (
any([
strings.replace_confusables(sender.display_name),
strings.replace_confusables(subject.subject),
// domain parts of sender
sender.email.local_part,
sender.email.domain.sld
],
// quick checks first
strings.icontains(., 'Capital One')
or strings.icontains(., 'CapitalOne')

// slower checks next
or regex.icontains(., 'Capital.?One')
// levenshtein distince similar to captial one
or strings.ilevenshtein(., 'Capital One') <= 2
)
or any(ml.logo_detect(beta.message_screenshot()).brands,
.name == "Capital One Bank" and .confidence != "low"
)
)
and not (
sender.email.domain.root_domain in (
"capitalone.co.uk",
"capitalone.com",
"capitaloneshopping.com",
"capitalonesoftware.com",
"capitalonebooking.com",
"capitalonetravel.com",
// "olbanking.com", // a fiserv.one domain
// "bynder.com", // Digital Assest Mgmt
// "gcs-web.com", // investor relations run by capital one
// "capitalonearena.com", // the arena
// "monumentalsports.com", // the company that owns a bunch of teams that play at the arena?
// "ticketmaster.com", // sell and advertises tickets at Capital One Arena
)
and headers.auth_summary.dmarc.pass
)
// and the sender is not from high trust sender root domains
and (
(
sender.email.domain.root_domain in $high_trust_sender_root_domains
and not headers.auth_summary.dmarc.pass
)
or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)
and // suspicious indicators here
(
// // password theme
(
strings.icontains(body.html.display_text, "new password")
or regex.icontains(body.html.display_text,
'(?:credentials?|password)\s*(?:\w+\s+){0,3}\s*(?:compromise|reset|expir(?:ation|ed)|update|invalid|incorrect|changed|(?:mis)?match)',
'(?:compromise|reset|expir(?:ation|ed)|update|invalid|incorrect|changed|(?:mis)?match)\s*(?:\w+\s+){0,3}\s*(?:credentials?|password)',
'(?:short|weak|chang(?:e|ing)|reset)\s*(?:\w+\s+){0,3}\s*(?:credentials?|password)',
'(?:credentials?|password)\s*(?:\w+\s+){0,3}\s*(?:short|weak|chang(?:e|ing)|reset)',
)
)
// // login failures
or (
strings.icontains(body.html.display_text, "unusual number of")
or strings.icontains(body.html.display_text, "security breach")
or (
strings.icontains(body.html.display_text, "security alert")
// some capital one notiifcaitons include directions to
// change notificaiton preferences to only security alerts
and (
strings.icount(body.html.display_text, "security alert") > strings.icount(body.html.display_text,
"sign in to your account and select Security Alerts."
)
)
)
or strings.icontains(body.html.display_text, "account remains secure")
or strings.icontains(body.html.display_text, "please verify your account")
or strings.icontains(body.html.display_text, "suspicious activity detected")
or strings.icontains(body.html.display_text, "temporarily locked out")
or regex.icontains(body.html.display_text,
'(?:invalid|unrecognized|unauthorized|fail(?:ed|ure)?|suspicious|unusual|attempt(?:ed)?|tried to)\s*(?:\w+\s+){0,3}\s*(?:log(?:.?in)?|sign(?:.?in)?|account|access|activity)',
'(?:log(?:.?in)?|sign(?:.?in)?|account|access|activity)\s*(?:\w+\s+){0,3}\s*(?:invalid|unrecognized|fail(?:ed|ure)?|suspicious|unusual|attempt(?:ed)?)'
)
)
// // account locked
or (
strings.icontains(body.html.display_text, "been suspend")
or strings.icontains(body.html.display_text, "to restore access")
or strings.icontains(body.html.display_text, "will be restored")
or strings.icontains(body.html.display_text, "security reasons")
or strings.icontains(body.html.display_text,
"temporarily restricted access"
)
or regex.icontains(body.html.display_text,
'acc(?:ou)?n?t\s*(?:\w+\s+){0,3}\s*(?:authenticat(?:e|ion)|activity|ho[li]d|terminat|[il1]{2}m[il1]t(?:s|ed|ation)|b?locked|de-?activat|suspen(?:ed|sion)|restrict(?:ed|ion)?|expir(?:ed?|ing)|v[il]o[li]at|verif(?:y|ication))',
'(?:authenticat(?:e|ion)|activity|ho[li]d|terminat|[il1]{2}m[il1]t(?:s|ed|ation)|b?locked|de-?activat|suspen(?:ed|sion)|restrict(?:ed|ion)?|expir(?:ed?|ing)|v[il]o[li]at|verif(?:y|ication))\s*(?:\w+\s+){0,3}\s*acc(?:ou)?n?t'
)
)
// // secure messages
or (
regex.icontains(body.html.display_text,
'(?:encrypt(?:ion|ed)?|secur(?:e|ity)) (?:\w+\s+){0,3}\s*message'
)
or strings.icontains(body.html.display_text, "document portal")
or regex.icontains(body.html.display_text,
"has been (?:encrypt|sent secure)"
)
or regex.icontains(body.html.display_text,
'encryption (?:\w+\s+){0,3}\s*tech'
)
)
// // documents to view
or (
// we can skip the regex if the diplay_text doesn't contain document
// this might need to be removed if the regex is expanded
strings.icontains(body.html.display_text, 'document')
and regex.icontains(body.html.display_text,
'document\s*(?:\w+\s+){0,3}\s*(?:ready|posted|review|available|online)',
'(?:ready|posted|review|available|online)\s*(?:\w+\s+){0,3}\s*document'
)
)
// // account/profile details
or (
strings.icontains(body.html.display_text, "about your account")
or strings.icontains(body.html.display_text, "action required")
or regex.icontains(body.html.display_text,
'(update|\bedit\b|modify|revise|verif(?:y|ication)|discrepanc(?:y|ies)|mismatch(?:es)?|inconsistenc(?:y|ies)?|difference(?:s)?|anomal(?:y|ies)?|irregularit(?:y|ies)?)\s*(?:\w+\s+){0,4}\s*(?:account|detail|record|data|info(?:rmation)?)',
'(?:account|detail|record|data|info(?:rmation)?)\s*(?:\w+\s+){0,4}\s*(update|\bedit\b|modify|revise|verif(?:y|ication)|discrepanc(?:y|ies)|mismatch(?:es)?|inconsistenc(?:y|ies)?|difference(?:s)?|anomal(?:y|ies)?|irregularit(?:y|ies)?)'
)
)
// // other calls to action that are unexpected
or (strings.icontains(body.html.display_text, "download the attachment"))

// the links contain suspect wording
or (
0 < length(body.links) <= 50
and any(body.links,
(
regex.icontains(.display_text, '(?:log|sign).?in')
or strings.icontains(.display_text, 'confirm')
or strings.icontains(.display_text, 'i recongize it')
or strings.icontains(.display_text, "something\'s wrong")
or regex.icontains(.display_text,
'(?:re)?view\s*(?:\w+\s*){0,3}\s*(?:document|message|now|account)'
)
or strings.icontains(.display_text, 'message')
or regex.icontains(.display_text,
'restore\s*(?:\w+\s*){0,3}\s*(?:account|access)'
)
)
and .href_url.domain.root_domain != "capitalone.com"
)
)
// the message contains a disclaimer but then isn't
or (
regex.icontains(body.current_thread.text,
'To ensure delivery, add [^\@]+@[^\s]*capitalone.com to your address book.'
)
and sender.email.domain.root_domain != "capitalone.com"
)
)
attack_types:
- "Credential Phishing"
tactics_and_techniques:
- "Impersonation: Brand"
- "Lookalike domain"
- "Social engineering"
detection_methods:
- "Computer Vision"
- "Sender analysis"
- "Header analysis"
id: "d53848e4-fc40-5bd1-ad5e-c9c4e85a669f"
Loading