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.
Sublime rule (View on GitHub)
1name: "QR Code with suspicious indicators"
2description: |
3 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.
4type: "rule"
5severity: "high"
6source: |
7 type.inbound
8 and (
9 (
10 length(attachments) <= 3
11 or (
12 any(attachments, length(ml.logo_detect(.).brands) > 0)
13 and length(attachments) <= 10
14 )
15 )
16 and (
17 any(attachments,
18 (.file_type in $file_types_images or .file_extension in $file_extensions_macros or .file_type == "pdf")
19 and any(file.explode(.),
20 .scan.qr.type is not null
21 and regex.contains(.scan.qr.data, '\.')
22 and not (strings.icontains(.scan.qr.data, ',') and .scan.qr.type == 'undefined')
23 // not a json string
24 and not (
25 strings.starts_with(.scan.qr.data, '{')
26 and strings.ends_with(.scan.qr.data, '}')
27 )
28 // exclude images taken with mobile cameras and screenshots from android
29 and not any(.scan.exiftool.fields,
30 .key == "Model"
31 or (
32 .key == "Software"
33 and strings.starts_with(.value, "Android")
34 )
35 )
36 // exclude images taken with mobile cameras and screenshots from Apple
37 and not any(.scan.exiftool.fields,
38 .key == "DeviceManufacturer"
39 and .value == "Apple Computer Inc."
40 )
41 // exclude images from WhatsApp (mobile)
42 and not regex.match(.file_name, 'WhatsApp Image \d\d\d\d-\d\d-\d\d at.*.jpe?g')
43 and not (
44 .scan.exiftool.image_height > 3000
45 or .scan.exiftool.image_width > 3000
46 )
47 // exclude contact cards
48 and not strings.istarts_with(.scan.qr.data, "BEGIN:VCARD")
49
50 // negate QR codes to legit Servicio de Administración Tributaria (SAT) Gov links
51 and not (
52 .scan.qr.url.domain.root_domain is not null
53 and .scan.qr.url.domain.root_domain in ('sat.gob.mx')
54 )
55 and not (
56 .scan.qr.data is not null
57 and strings.icontains(.scan.qr.data, 'sat.gob.mx')
58 )
59
60 )
61 )
62 or (
63 length(attachments) == 0
64 and any(file.explode(beta.message_screenshot()),
65 .scan.exiftool.image_height < 2000
66 and .scan.exiftool.image_width < 2000
67 and .scan.qr.type is not null
68 and regex.contains(.scan.qr.data, '\.')
69 // exclude contact cards
70 and not strings.istarts_with(.scan.qr.data, "BEGIN:VCARD")
71 )
72 )
73 )
74 and (
75 any(recipients.to,
76 strings.icontains(sender.display_name, .email.domain.sld)
77 )
78 or length(body.current_thread.text) is null
79 or (
80 body.current_thread.text == ""
81 and (
82 (
83 (
84 length(headers.references) > 0
85 or headers.in_reply_to is null
86 )
87 and not (
88 (
89 strings.istarts_with(subject.subject, "RE:")
90 or strings.istarts_with(subject.subject, "RES:")
91 or strings.istarts_with(subject.subject, "R:")
92 or strings.istarts_with(subject.subject, "ODG:")
93 or strings.istarts_with(subject.subject, "答复:")
94 or strings.istarts_with(subject.subject, "AW:")
95 or strings.istarts_with(subject.subject, "TR:")
96 or strings.istarts_with(subject.subject, "FWD:")
97 or regex.imatch(subject.subject,
98 '(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
99 )
100 )
101 )
102 )
103 or length(headers.references) == 0
104 )
105 )
106 or regex.contains(subject.subject,
107 "(Authenticat(e|or|ion)|2fa|Multi.Factor|(qr|bar).code|action.require|alert|Att(n|ention):)"
108 )
109 or (any(recipients.to, strings.icontains(subject.subject, .display_name)))
110 or (
111 regex.icontains(subject.subject,
112 "termination.*notice",
113 "38417",
114 ":completed",
115 "[il1]{2}mit.*ma[il1]{2} ?bo?x",
116 "[il][il][il]egai[ -]",
117 "[li][li][li]ega[li] attempt",
118 "[ng]-?[io]n .*block",
119 "[ng]-?[io]n .*cancel",
120 "[ng]-?[io]n .*deactiv",
121 "[ng]-?[io]n .*disabl",
122 "action.*required",
123 "abandon.*package",
124 "about.your.account",
125 "acc(ou)?n?t (is )?on ho[li]d",
126 "acc(ou)?n?t.*terminat",
127 "acc(oun)?t.*[il1]{2}mitation",
128 "access.*limitation",
129 "account (will be )?block",
130 "account.*de-?activat",
131 "account.*locked",
132 "account.*re-verification",
133 "account.*security",
134 "account.*suspension",
135 "account.has.been",
136 "account.has.expired",
137 "account.will.be.blocked",
138 "account v[il]o[li]at",
139 "activity.*acc(oun)?t",
140 "almost.full",
141 "app[li]e.[il]d",
142 "authenticate.*account",
143 "been.*suspend",
144 "clos.*of.*account.*processed",
145 "confirm.your.account",
146 "courier.*able",
147 "crediential.*notif",
148 "deactivation.*in.*progress",
149 "delivery.*attempt.*failed",
150 "document.received",
151 "documented.*shared.*with.*you",
152 "dropbox.*document",
153 "e-?ma[il1]+ .{010}suspen",
154 "e-?ma[il1]{1} user",
155 "e-?ma[il1]{2} acc",
156 "e-?ma[il1]{2}.*up.?grade",
157 "e.?ma[il1]{2}.*server",
158 "e.?ma[il1]{2}.*suspend",
159 "email.update",
160 "faxed you",
161 "fraud(ulent)?.*charge",
162 "from.helpdesk",
163 "fu[il1]{2}.*ma[il1]+[ -]?box",
164 "has.been.*suspended",
165 "has.been.limited",
166 "have.locked",
167 "he[li]p ?desk upgrade",
168 "heipdesk",
169 "i[il]iega[il]",
170 "ii[il]ega[il]",
171 "incoming e?mail",
172 "incoming.*fax",
173 "lock.*security",
174 "ma[il1]{1}[ -]?box.*quo",
175 "ma[il1]{2}[ -]?box.*fu[il1]",
176 "ma[il1]{2}box.*[il1]{2}mit",
177 "ma[il1]{2}box stor",
178 "mail on.?hold",
179 "mail.*box.*migration",
180 "mail.*de-?activat",
181 "mail.update.required",
182 "mails.*pending",
183 "messages.*pending",
184 "missed.*shipping.*notification",
185 "missed.shipment.notification",
186 "must.update.your.account",
187 "new [sl][io]g?[nig][ -]?in from",
188 "new voice ?-?mail",
189 "notifications.*pending",
190 "office.*3.*6.*5.*suspend",
191 "office365",
192 "on google docs with you",
193 "online doc",
194 "password.*compromised",
195 "payment advice",
196 "periodic maintenance",
197 "potential(ly)? unauthorized",
198 "refund not approved",
199 "report",
200 "revised.*policy",
201 "scam",
202 "scanned.?invoice",
203 "secured?.update",
204 "security breach",
205 "securlty",
206 "seguranca",
207 "signed.*delivery",
208 "status of your .{314}? ?delivery",
209 "susp[il1]+c[il1]+ous.*act[il1]+v[il1]+ty",
210 "suspicious.*sign.*[io]n",
211 "suspicious.activit",
212 "temporar(il)?y deactivate",
213 "temporar[il1]{2}y disab[li]ed",
214 "temporarily.*lock",
215 "un-?usua[li].activity",
216 "unable.*deliver",
217 "unauthorized.*activit",
218 "unauthorized.device",
219 "undelivered message",
220 "unread.*doc",
221 "unusual.activity",
222 "upgrade.*account",
223 "upgrade.notice",
224 "urgent message",
225 "urgent.verification",
226 "v[il1]o[li1]at[il1]on security",
227 "va[il1]{1}date.*ma[il1]{2}[ -]?box",
228 "verification ?-?require",
229 "verification( )?-?need",
230 "verify.your?.account",
231 "web ?-?ma[il1]{2}",
232 "web[ -]?ma[il1]{2}",
233 "will.be.suspended",
234 "your (customer )?account .as",
235 "your.office.365",
236 "your.online.access"
237 )
238 or any($suspicious_subjects, strings.icontains(subject.subject, .))
239 or regex.icontains(sender.display_name,
240 "Accounts.?Payable",
241 "Admin",
242 "Administrator",
243 "Alert",
244 "Assistant",
245 "Billing",
246 "Benefits",
247 "Bonus",
248 "CEO",
249 "CFO",
250 "CIO",
251 "CTO",
252 "Chairman",
253 "Claim",
254 "Confirm",
255 "Critical",
256 "Customer Service",
257 "Deal",
258 "Discount",
259 "Director",
260 "Exclusive",
261 "Executive",
262 "Fax",
263 "Free",
264 "Gift",
265 "/bHR/b",
266 "Helpdesk",
267 "Human Resources",
268 "Immediate",
269 "Important",
270 "Info",
271 "Information",
272 "Invoice",
273 '\bIT\b',
274 "Legal",
275 "Lottery",
276 "Management",
277 "Manager",
278 "Member Services",
279 "Notification",
280 "Offer",
281 "Operations",
282 "Order",
283 "Partner",
284 "Payment",
285 "Payroll",
286 "President",
287 "Premium",
288 "Prize",
289 "Receipt",
290 "Refund",
291 "Registrar",
292 "Required",
293 "Reward",
294 "Sales",
295 "Secretary",
296 "Security",
297 "Service",
298 "Signature",
299 "Storage",
300 "Support",
301 "Sweepstakes",
302 "System",
303 "Tax",
304 "Tech Support",
305 "Update",
306 "Upgrade",
307 "Urgent",
308 "Validate",
309 "Verify",
310 "VIP",
311 "Webmaster",
312 "Winner",
313 )
314 )
315 or (
316 (
317 length(recipients.to) == 0
318 or all(recipients.to, .display_name == "Undisclosed recipients")
319 )
320 and length(recipients.cc) == 0
321 and length(recipients.bcc) == 0
322 )
323 or any(file.explode(beta.message_screenshot()),
324 (
325 .scan.qr.url.domain.tld in $suspicious_tlds
326 and .scan.qr.url.domain.root_domain != "app.link"
327 )
328 or
329 // linkanalysis phishing disposition
330 any([ml.link_analysis(.scan.qr.url)],
331 .credphish.disposition == "phishing"
332 )
333 )
334 or any(attachments,
335 (.file_type in $file_types_images or .file_extension in $file_extensions_macros or .file_type == "pdf")
336 and any(file.explode(.),
337 (
338 .scan.qr.url.domain.tld in $suspicious_tlds
339 and .scan.qr.url.domain.root_domain != "app.link"
340 and .scan.qr.url.domain.root_domain != "qr.link"
341 )
342 and .scan.qr.url.domain.root_domain not in $org_domains
343 )
344 )
345 or sender.email.domain.tld in $suspicious_tlds
346 )
347 )
348
349 // sender profile is new or outlier
350 and (
351 (
352 profile.by_sender().prevalence in ("new", "outlier")
353 and not profile.by_sender().solicited
354 )
355 or profile.by_sender().any_messages_malicious_or_spam
356 )
357
358 and not profile.by_sender().any_false_positives
359
360 // negate highly trusted sender domains unless they fail DMARC authentication
361 and (
362 (
363 sender.email.domain.root_domain in $high_trust_sender_root_domains
364 and not headers.auth_summary.dmarc.pass
365 )
366 or sender.email.domain.root_domain not in $high_trust_sender_root_domains
367 )
368attack_types:
369 - "Credential Phishing"
370tactics_and_techniques:
371 - "QR code"
372 - "Social engineering"
373detection_methods:
374 - "Content analysis"
375 - "Header analysis"
376 - "Computer Vision"
377 - "Natural Language Understanding"
378 - "QR code analysis"
379 - "Sender analysis"
380 - "URL analysis"
381id: "04f5c34f-6518-512d-916c-4c2c2827c6a9"