← Back to Explore
sublimehighRule
QR Code with suspicious indicators
This rule flags messages with QR codes in attachments when there are three or fewer attachments. If no attachments are present, the rule captures a screenshot of the message for analysis. Additional triggers include: sender's name containing the recipient's SLD, recipient's email mentioned in the body, an empty message body, a suspicious subject, or undisclosed recipients.
Detection Query
type.inbound
and (
(
length(attachments) <= 3
or (
any(attachments, length(ml.logo_detect(.).brands) > 0)
and length(attachments) <= 10
)
)
and (
any(attachments,
(
.file_type in $file_types_images
or .file_extension in $file_extensions_macros
or .file_type == "pdf"
)
and (
any(file.explode(.),
(
.scan.qr.type is not null
and regex.contains(.scan.qr.data, '\.')
and not strings.starts_with(.scan.qr.data,
"https://qr.skyqr.co.za/"
)
and not (
strings.icontains(.scan.qr.data, ',')
and .scan.qr.type == 'undefined'
)
// not a json string
and not (
strings.starts_with(.scan.qr.data, '{')
and strings.ends_with(.scan.qr.data, '}')
)
// exclude images taken with mobile cameras and screenshots from android
and not any(.scan.exiftool.fields,
.key == "Model"
or (
.key == "Software"
and strings.starts_with(.value, "Android")
)
)
// exclude images taken with mobile cameras and screenshots from Apple
and not any(.scan.exiftool.fields,
.key == "DeviceManufacturer"
and .value == "Apple Computer Inc."
)
// exclude images from WhatsApp (mobile)
and not regex.match(.file_name,
'WhatsApp Image \d\d\d\d-\d\d-\d\d at.*.jpe?g'
)
and not (
(
.scan.exiftool.image_height > 3000
and .scan.exiftool.image_height is not null
)
or (
.scan.exiftool.image_width > 3000
and .scan.exiftool.image_width is not null
)
)
// exclude contact cards
and not strings.istarts_with(.scan.qr.data, "BEGIN:VCARD")
// negate QR codes to legit Servicio de Administración Tributaria (SAT) Gov links
and not (
.scan.qr.url.domain.root_domain is not null
and .scan.qr.url.domain.root_domain in ('sat.gob.mx')
)
and not (
.scan.qr.data is not null
and strings.icontains(.scan.qr.data, 'sat.gob.mx')
)
)
or (
regex.icontains(.scan.ocr.raw,
'(?:scan|camera|review and sign)'
)
and regex.icontains(.scan.ocr.raw, '(?:\bQR\b|Q\.R\.|barcode)')
)
)
)
)
or (
length(attachments) == 0
//
// This rule makes use of a beta feature and is subject to change without notice
// using the beta feature in custom rules is not suggested until it has been formally released
//
and (
(
beta.parse_exif(file.message_screenshot()).image_height < 2000
and beta.parse_exif(file.message_screenshot()).image_width < 2000
)
// ignore image height/width if there is excessive whitespace padding
or regex.contains(body.html.raw, '\"padding:0px 0px \d{3,4}px 0px')
)
and any(beta.scan_qr(file.message_screenshot()).items,
.type is not null
and regex.contains(.data, '\.')
// exclude contact cards
and not strings.istarts_with(.data, "BEGIN:VCARD")
)
)
)
and (
any(recipients.to,
strings.icontains(sender.display_name, .email.domain.sld)
)
or length(body.current_thread.text) is null
or (
body.current_thread.text == ""
and (
(
(length(headers.references) > 0 or headers.in_reply_to is null)
and not (
(
strings.istarts_with(subject.subject, "RE:")
or strings.istarts_with(subject.subject, "RES:")
or strings.istarts_with(subject.subject, "R:")
or strings.istarts_with(subject.subject, "ODG:")
or strings.istarts_with(subject.subject, "答复:")
or strings.istarts_with(subject.subject, "AW:")
or strings.istarts_with(subject.subject, "TR:")
or strings.istarts_with(subject.subject, "FWD:")
or regex.imatch(subject.subject,
'(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
)
)
)
)
or length(headers.references) == 0
)
)
or regex.contains(subject.subject,
"(Authenticat(e|or|ion)|2fa|Multi.Factor|(qr|bar).code|action.require|alert|Att(n|ention):)"
)
or (any(recipients.to, strings.icontains(subject.subject, .display_name)))
or (
regex.icontains(subject.subject,
"termination.*notice",
"38417",
":completed",
"[il1]{2}mit.*ma[il1]{2} ?bo?x",
"[il][il][il]egai[ -]",
"[li][li][li]ega[li] attempt",
"[ng]-?[io]n .*block",
"[ng]-?[io]n .*cancel",
"[ng]-?[io]n .*deactiv",
"[ng]-?[io]n .*disabl",
"action.*required",
"abandon.*package",
"about.your.account",
"acc(ou)?n?t (is )?on ho[li]d",
"acc(ou)?n?t.*terminat",
"acc(oun)?t.*[il1]{2}mitation",
"access.*limitation",
"account (will be )?block",
"account.*de-?activat",
"account.*locked",
"account.*re-verification",
"account.*security",
"account.*suspension",
"account.has.been",
"account.has.expired",
"account.will.be.blocked",
"account v[il]o[li]at",
"activity.*acc(oun)?t",
"almost.full",
"app[li]e.[il]d",
"authenticate.*account",
"been.*suspend",
"bonus",
"clos.*of.*account.*processed",
"confirm.your.account",
"courier.*able",
"crediential.*notif",
"deactivation.*in.*progress",
"delivery.*attempt.*failed",
"document.received",
"documented.*shared.*with.*you",
"dropbox.*document",
"e-?ma[il1]+ .{010}suspen",
"e-?ma[il1]{1} user",
"e-?ma[il1]{2} acc",
"e-?ma[il1]{2}.*up.?grade",
"e.?ma[il1]{2}.*server",
"e.?ma[il1]{2}.*suspend",
"email.update",
"faxed you",
"fraud(ulent)?.*charge",
"from.helpdesk",
"fu[il1]{2}.*ma[il1]+[ -]?box",
"has.been.*suspended",
"has.been.limited",
"have.locked",
"he[li]p ?desk upgrade",
"heipdesk",
"i[il]iega[il]",
"ii[il]ega[il]",
"incoming e?mail",
"incoming.*fax",
"lock.*security",
"ma[il1]{1}[ -]?box.*quo",
"ma[il1]{2}[ -]?box.*fu[il1]",
"ma[il1]{2}box.*[il1]{2}mit",
"ma[il1]{2}box stor",
"mail on.?hold",
"mail.*box.*migration",
"mail.*de-?activat",
"mail.update.required",
"mails.*pending",
"messages.*pending",
"missed.*shipping.*notification",
"missed.shipment.notification",
"must.update.your.account",
"new [sl][io]g?[nig][ -]?in from",
"new voice ?-?mail",
"notifications.*pending",
"office.*3.*6.*5.*suspend",
"office365",
"on google docs with you",
"online doc",
"password.*compromised",
"payment advice",
"periodic maintenance",
"potential(ly)? unauthorized",
"refund not approved",
"report",
"revised.*policy",
"scam",
"scanned.?invoice",
"secured?.update",
"security breach",
"securlty",
"seguranca",
"signed.*delivery",
"status of your .{314}? ?delivery",
"susp[il1]+c[il1]+ous.*act[il1]+v[il1]+ty",
"suspicious.*sign.*[io]n",
"suspicious.activit",
"temporar(il)?y deactivate",
"temporar[il1]{2}y disab[li]ed",
"temporarily.*lock",
"un-?usua[li].activity",
"unable.*deliver",
"unauthorized.*activit",
"unauthorized.device",
"undelivered message",
"unread.*doc",
"unusual.activity",
"upgrade.*account",
"upgrade.notice",
"urgent message",
"urgent.verification",
"v[il1]o[li1]at[il1]on security",
"va[il1]{1}date.*ma[il1]{2}[ -]?box",
"verification ?-?require",
"verification( )?-?need",
"verify.your?.account",
"web ?-?ma[il1]{2}",
"web[ -]?ma[il1]{2}",
"will.be.suspended",
"your (customer )?account .as",
"your.office.365",
"your.online.access"
)
or any($suspicious_subjects, strings.icontains(subject.subject, .))
or regex.icontains(sender.display_name,
"Accounts.?Payable",
"Admin",
"Administrator",
"Alert",
"Assistant",
"Billing",
"Benefits",
"Bonus",
"CEO",
"CFO",
"CIO",
"CTO",
"Chairman",
"Claim",
"Confirm",
"Critical",
"Customer Service",
"Deal",
"Discount",
"Director",
"Exclusive",
"Executive",
"Fax",
"Free",
"Gift",
"/bHR/b",
"Helpdesk",
"Human Resources",
"Immediate",
"Important",
"Info",
"Information",
"Invoice",
'\bIT\b',
"Legal",
"Lottery",
"Management",
"Manager",
"Member Services",
"Notification",
"Offer",
"Operations",
"Order",
"Partner",
"Payment",
"Payroll",
"President",
"Premium",
"Prize",
"Receipt",
"Refund",
"Registrar",
"Required",
"Reward",
"Sales",
"Secretary",
"Security",
"Service",
"Signature",
"Storage",
"Support",
"Sweepstakes",
"System",
"Tax",
"Tech Support",
"Update",
"Upgrade",
"Urgent",
"Validate",
"Verify",
"VIP",
"Webmaster",
"Winner",
)
)
or (
(
length(recipients.to) == 0
or all(recipients.to, .display_name == "Undisclosed recipients")
)
and length(recipients.cc) == 0
and length(recipients.bcc) == 0
)
or any(beta.scan_qr(file.message_screenshot()).items,
(
.url.domain.tld in $suspicious_tlds
and .url.domain.root_domain != "app.link"
)
// linkanalysis phishing disposition
or ml.link_analysis(.url).credphish.disposition == "phishing"
)
or any(attachments,
(
.file_type in $file_types_images
or .file_extension in $file_extensions_macros
or .file_type == "pdf"
)
and any(file.explode(.),
(
.scan.qr.url.domain.tld in $suspicious_tlds
and .scan.qr.url.domain.root_domain != "app.link"
and .scan.qr.url.domain.root_domain != "qr.link"
and .scan.qr.url.domain.root_domain != "skyqr.co.za"
)
and .scan.qr.url.domain.root_domain not in $org_domains
)
)
or sender.email.domain.tld in $suspicious_tlds
)
)
// sender profile is new or outlier
and (
profile.by_sender_email().any_messages_malicious_or_spam
or (
sender.email.domain.domain in $org_domains
and not coalesce(headers.auth_summary.dmarc.pass, false)
)
or (
profile.by_sender_email().prevalence in ("new", "outlier")
and not profile.by_sender_email().solicited
)
)
and not profile.by_sender_email().any_messages_benign
// negate highly trusted sender domains unless they fail DMARC authentication
and not (
sender.email.domain.root_domain in $high_trust_sender_root_domains
and coalesce(headers.auth_summary.dmarc.pass, false)
)
Data Sources
Email MessagesEmail HeadersEmail Attachments
Platforms
email
Raw Content
name: "QR Code with suspicious indicators"
description: |
This rule flags messages with QR codes in attachments when there are three or fewer attachments. If no attachments are present, the rule captures a screenshot of the message for analysis. Additional triggers include: sender's name containing the recipient's SLD, recipient's email mentioned in the body, an empty message body, a suspicious subject, or undisclosed recipients.
type: "rule"
severity: "high"
source: |
type.inbound
and (
(
length(attachments) <= 3
or (
any(attachments, length(ml.logo_detect(.).brands) > 0)
and length(attachments) <= 10
)
)
and (
any(attachments,
(
.file_type in $file_types_images
or .file_extension in $file_extensions_macros
or .file_type == "pdf"
)
and (
any(file.explode(.),
(
.scan.qr.type is not null
and regex.contains(.scan.qr.data, '\.')
and not strings.starts_with(.scan.qr.data,
"https://qr.skyqr.co.za/"
)
and not (
strings.icontains(.scan.qr.data, ',')
and .scan.qr.type == 'undefined'
)
// not a json string
and not (
strings.starts_with(.scan.qr.data, '{')
and strings.ends_with(.scan.qr.data, '}')
)
// exclude images taken with mobile cameras and screenshots from android
and not any(.scan.exiftool.fields,
.key == "Model"
or (
.key == "Software"
and strings.starts_with(.value, "Android")
)
)
// exclude images taken with mobile cameras and screenshots from Apple
and not any(.scan.exiftool.fields,
.key == "DeviceManufacturer"
and .value == "Apple Computer Inc."
)
// exclude images from WhatsApp (mobile)
and not regex.match(.file_name,
'WhatsApp Image \d\d\d\d-\d\d-\d\d at.*.jpe?g'
)
and not (
(
.scan.exiftool.image_height > 3000
and .scan.exiftool.image_height is not null
)
or (
.scan.exiftool.image_width > 3000
and .scan.exiftool.image_width is not null
)
)
// exclude contact cards
and not strings.istarts_with(.scan.qr.data, "BEGIN:VCARD")
// negate QR codes to legit Servicio de Administración Tributaria (SAT) Gov links
and not (
.scan.qr.url.domain.root_domain is not null
and .scan.qr.url.domain.root_domain in ('sat.gob.mx')
)
and not (
.scan.qr.data is not null
and strings.icontains(.scan.qr.data, 'sat.gob.mx')
)
)
or (
regex.icontains(.scan.ocr.raw,
'(?:scan|camera|review and sign)'
)
and regex.icontains(.scan.ocr.raw, '(?:\bQR\b|Q\.R\.|barcode)')
)
)
)
)
or (
length(attachments) == 0
//
// This rule makes use of a beta feature and is subject to change without notice
// using the beta feature in custom rules is not suggested until it has been formally released
//
and (
(
beta.parse_exif(file.message_screenshot()).image_height < 2000
and beta.parse_exif(file.message_screenshot()).image_width < 2000
)
// ignore image height/width if there is excessive whitespace padding
or regex.contains(body.html.raw, '\"padding:0px 0px \d{3,4}px 0px')
)
and any(beta.scan_qr(file.message_screenshot()).items,
.type is not null
and regex.contains(.data, '\.')
// exclude contact cards
and not strings.istarts_with(.data, "BEGIN:VCARD")
)
)
)
and (
any(recipients.to,
strings.icontains(sender.display_name, .email.domain.sld)
)
or length(body.current_thread.text) is null
or (
body.current_thread.text == ""
and (
(
(length(headers.references) > 0 or headers.in_reply_to is null)
and not (
(
strings.istarts_with(subject.subject, "RE:")
or strings.istarts_with(subject.subject, "RES:")
or strings.istarts_with(subject.subject, "R:")
or strings.istarts_with(subject.subject, "ODG:")
or strings.istarts_with(subject.subject, "答复:")
or strings.istarts_with(subject.subject, "AW:")
or strings.istarts_with(subject.subject, "TR:")
or strings.istarts_with(subject.subject, "FWD:")
or regex.imatch(subject.subject,
'(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
)
)
)
)
or length(headers.references) == 0
)
)
or regex.contains(subject.subject,
"(Authenticat(e|or|ion)|2fa|Multi.Factor|(qr|bar).code|action.require|alert|Att(n|ention):)"
)
or (any(recipients.to, strings.icontains(subject.subject, .display_name)))
or (
regex.icontains(subject.subject,
"termination.*notice",
"38417",
":completed",
"[il1]{2}mit.*ma[il1]{2} ?bo?x",
"[il][il][il]egai[ -]",
"[li][li][li]ega[li] attempt",
"[ng]-?[io]n .*block",
"[ng]-?[io]n .*cancel",
"[ng]-?[io]n .*deactiv",
"[ng]-?[io]n .*disabl",
"action.*required",
"abandon.*package",
"about.your.account",
"acc(ou)?n?t (is )?on ho[li]d",
"acc(ou)?n?t.*terminat",
"acc(oun)?t.*[il1]{2}mitation",
"access.*limitation",
"account (will be )?block",
"account.*de-?activat",
"account.*locked",
"account.*re-verification",
"account.*security",
"account.*suspension",
"account.has.been",
"account.has.expired",
"account.will.be.blocked",
"account v[il]o[li]at",
"activity.*acc(oun)?t",
"almost.full",
"app[li]e.[il]d",
"authenticate.*account",
"been.*suspend",
"bonus",
"clos.*of.*account.*processed",
"confirm.your.account",
"courier.*able",
"crediential.*notif",
"deactivation.*in.*progress",
"delivery.*attempt.*failed",
"document.received",
"documented.*shared.*with.*you",
"dropbox.*document",
"e-?ma[il1]+ .{010}suspen",
"e-?ma[il1]{1} user",
"e-?ma[il1]{2} acc",
"e-?ma[il1]{2}.*up.?grade",
"e.?ma[il1]{2}.*server",
"e.?ma[il1]{2}.*suspend",
"email.update",
"faxed you",
"fraud(ulent)?.*charge",
"from.helpdesk",
"fu[il1]{2}.*ma[il1]+[ -]?box",
"has.been.*suspended",
"has.been.limited",
"have.locked",
"he[li]p ?desk upgrade",
"heipdesk",
"i[il]iega[il]",
"ii[il]ega[il]",
"incoming e?mail",
"incoming.*fax",
"lock.*security",
"ma[il1]{1}[ -]?box.*quo",
"ma[il1]{2}[ -]?box.*fu[il1]",
"ma[il1]{2}box.*[il1]{2}mit",
"ma[il1]{2}box stor",
"mail on.?hold",
"mail.*box.*migration",
"mail.*de-?activat",
"mail.update.required",
"mails.*pending",
"messages.*pending",
"missed.*shipping.*notification",
"missed.shipment.notification",
"must.update.your.account",
"new [sl][io]g?[nig][ -]?in from",
"new voice ?-?mail",
"notifications.*pending",
"office.*3.*6.*5.*suspend",
"office365",
"on google docs with you",
"online doc",
"password.*compromised",
"payment advice",
"periodic maintenance",
"potential(ly)? unauthorized",
"refund not approved",
"report",
"revised.*policy",
"scam",
"scanned.?invoice",
"secured?.update",
"security breach",
"securlty",
"seguranca",
"signed.*delivery",
"status of your .{314}? ?delivery",
"susp[il1]+c[il1]+ous.*act[il1]+v[il1]+ty",
"suspicious.*sign.*[io]n",
"suspicious.activit",
"temporar(il)?y deactivate",
"temporar[il1]{2}y disab[li]ed",
"temporarily.*lock",
"un-?usua[li].activity",
"unable.*deliver",
"unauthorized.*activit",
"unauthorized.device",
"undelivered message",
"unread.*doc",
"unusual.activity",
"upgrade.*account",
"upgrade.notice",
"urgent message",
"urgent.verification",
"v[il1]o[li1]at[il1]on security",
"va[il1]{1}date.*ma[il1]{2}[ -]?box",
"verification ?-?require",
"verification( )?-?need",
"verify.your?.account",
"web ?-?ma[il1]{2}",
"web[ -]?ma[il1]{2}",
"will.be.suspended",
"your (customer )?account .as",
"your.office.365",
"your.online.access"
)
or any($suspicious_subjects, strings.icontains(subject.subject, .))
or regex.icontains(sender.display_name,
"Accounts.?Payable",
"Admin",
"Administrator",
"Alert",
"Assistant",
"Billing",
"Benefits",
"Bonus",
"CEO",
"CFO",
"CIO",
"CTO",
"Chairman",
"Claim",
"Confirm",
"Critical",
"Customer Service",
"Deal",
"Discount",
"Director",
"Exclusive",
"Executive",
"Fax",
"Free",
"Gift",
"/bHR/b",
"Helpdesk",
"Human Resources",
"Immediate",
"Important",
"Info",
"Information",
"Invoice",
'\bIT\b',
"Legal",
"Lottery",
"Management",
"Manager",
"Member Services",
"Notification",
"Offer",
"Operations",
"Order",
"Partner",
"Payment",
"Payroll",
"President",
"Premium",
"Prize",
"Receipt",
"Refund",
"Registrar",
"Required",
"Reward",
"Sales",
"Secretary",
"Security",
"Service",
"Signature",
"Storage",
"Support",
"Sweepstakes",
"System",
"Tax",
"Tech Support",
"Update",
"Upgrade",
"Urgent",
"Validate",
"Verify",
"VIP",
"Webmaster",
"Winner",
)
)
or (
(
length(recipients.to) == 0
or all(recipients.to, .display_name == "Undisclosed recipients")
)
and length(recipients.cc) == 0
and length(recipients.bcc) == 0
)
or any(beta.scan_qr(file.message_screenshot()).items,
(
.url.domain.tld in $suspicious_tlds
and .url.domain.root_domain != "app.link"
)
// linkanalysis phishing disposition
or ml.link_analysis(.url).credphish.disposition == "phishing"
)
or any(attachments,
(
.file_type in $file_types_images
or .file_extension in $file_extensions_macros
or .file_type == "pdf"
)
and any(file.explode(.),
(
.scan.qr.url.domain.tld in $suspicious_tlds
and .scan.qr.url.domain.root_domain != "app.link"
and .scan.qr.url.domain.root_domain != "qr.link"
and .scan.qr.url.domain.root_domain != "skyqr.co.za"
)
and .scan.qr.url.domain.root_domain not in $org_domains
)
)
or sender.email.domain.tld in $suspicious_tlds
)
)
// sender profile is new or outlier
and (
profile.by_sender_email().any_messages_malicious_or_spam
or (
sender.email.domain.domain in $org_domains
and not coalesce(headers.auth_summary.dmarc.pass, false)
)
or (
profile.by_sender_email().prevalence in ("new", "outlier")
and not profile.by_sender_email().solicited
)
)
and not profile.by_sender_email().any_messages_benign
// negate highly trusted sender domains unless they fail DMARC authentication
and not (
sender.email.domain.root_domain in $high_trust_sender_root_domains
and coalesce(headers.auth_summary.dmarc.pass, false)
)
attack_types:
- "Credential Phishing"
tactics_and_techniques:
- "QR code"
- "Social engineering"
detection_methods:
- "Content analysis"
- "Header analysis"
- "Computer Vision"
- "Natural Language Understanding"
- "QR code analysis"
- "Sender analysis"
- "URL analysis"
id: "04f5c34f-6518-512d-916c-4c2c2827c6a9"