Attachment: QR code with credential phishing indicators

Detects messages with between 1-3 attachments containing a QR code with suspicious credential theft indicators, such as: LinkAnalysis credential phishing conclusion, decoded QR code url traverses suspicious infrastructure, the final destination is in URLhaus, decoded URL downloads a zip or executable, leverages URL shorteners, known QR abused openredirects, and more.

Sublime rule (View on GitHub)

  1name: "Attachment: QR code with credential phishing indicators"
  2description: |
  3    Detects messages with between 1-3 attachments containing a QR code with suspicious credential theft indicators, such as: LinkAnalysis credential phishing conclusion, decoded QR code url traverses suspicious infrastructure, the final destination is in URLhaus, decoded  URL downloads a zip or executable, leverages URL shorteners, known QR abused openredirects, and more. 
  4type: "rule"
  5severity: "medium"
  6source: |
  7  type.inbound
  8  and (
  9    1 <= length(attachments) < 3
 10    or (
 11      // if there are more than three attachments
 12      3 <= length(attachments) < 20
 13      // there are only pngs and pdf/docx
 14      and length(distinct(map(attachments, .file_extension))) == 2
 15      and all(distinct(map(attachments, .file_extension)),
 16              . in ('png', 'pdf', 'docx')
 17      )
 18      and (
 19        // multiple attachments mention common brands or other common common filenames
 20        (
 21          length(filter(attachments,
 22                        strings.icontains(.file_name, 'adobe')
 23                        or strings.icontains(.file_name, 'office')
 24                        or strings.icontains(.file_name, 'appstore')
 25                        or strings.icontains(.file_name, 'google')
 26                        or strings.icontains(.file_name, 'padlock')
 27                        or regex.icontains(.file_name, '\bdoc\b')
 28                 )
 29          ) > 3
 30        )
 31        // the attachment name contains the SLD of a recipient
 32        or any(filter(attachments, .file_extension in ('pdf', 'docx')),
 33               any(filter(recipients.to, .email.domain.valid),
 34                   strings.icontains(..file_name, .email.domain.sld)
 35               )
 36        )
 37      )
 38    )
 39  )
 40  
 41  // Inspects image attachments for QR codes
 42  and any(attachments,
 43          (
 44            .file_type in $file_types_images
 45            or .file_type == "pdf"
 46            or .file_extension in $file_extensions_macros
 47          )
 48          and (
 49            any(file.explode(.),
 50                .scan.qr.type == "url"
 51                and not .scan.qr.url.domain.domain == "geico.app.link"
 52                and (
 53                  // pass the QR URL to LinkAnalysis
 54                  any([ml.link_analysis(.scan.qr.url)],
 55                      .credphish.disposition == "phishing"
 56  
 57                      // any routing traverses via $suspicious_tld list
 58                      or any(.redirect_history, .domain.tld in $suspicious_tlds)
 59  
 60                      // effective destination in $suspicious_tld list
 61                      or .effective_url.domain.tld in $suspicious_tlds
 62  
 63                      // or the effective destination domain is in $abuse_ch_urlhaus_domains_trusted_reporters
 64                      or .effective_url.domain.root_domain in $abuse_ch_urlhaus_domains_trusted_reporters
 65  
 66                      // or any files downloaded are zips or executables
 67                      or any(.files_downloaded,
 68                             .file_extension in $file_extensions_common_archives
 69                             or .file_extension in $file_extensions_executables
 70                      )
 71                  )
 72                  or (
 73  
 74                    // or the QR code's root domain is a url_shortener
 75                    .scan.qr.url.domain.root_domain in $url_shorteners
 76                    or (
 77                      .scan.qr.url.domain.root_domain in $social_landing_hosts
 78                      and (
 79                        not (
 80                          any(ml.nlu_classifier(body.current_thread.text).intents,
 81                              .name == "benign"
 82                          )
 83                          or any(ml.nlu_classifier(body.current_thread.text).entities,
 84                                 .name == "disclaimer"
 85                          )
 86                        )
 87                        or not any(attachments,
 88                                   any(file.explode(.),
 89                                       any(ml.nlu_classifier(.scan.ocr.raw).intents,
 90                                           .name == "benign"
 91                                       )
 92                                   )
 93                        )
 94                        // the QR code contains the email address of a recipient
 95                        or (
 96                          any(filter(recipients.to, .email.domain.valid),
 97                              strings.icontains(..scan.qr.url.url, .email.email)
 98                              or strings.decode_hex(..scan.qr.url.fragment) == .email.email
 99                          )
100                        )
101                      )
102  
103                      // exclude google maps
104                      and not strings.starts_with(.scan.qr.url.url,
105                                                  'https://goo.gl/maps'
106                      )
107                      and not strings.starts_with(.scan.qr.url.url,
108                                                  'https://maps.app.goo.gl'
109                      )
110                    )
111                  )
112  
113                  // the QR code url is a bing open redirect
114                  or (
115                    .scan.qr.url.domain.root_domain == 'bing.com'
116                    and .scan.qr.url.path =~ '/ck/a'
117                  )
118                  // QR code contains non ascii chars
119                  or regex.contains(.scan.qr.url.url, '[^\x00-\x7F]')
120                  or (
121                    (
122                      // usap-dc open redirect
123                      .scan.qr.url.domain.root_domain == "usap-dc.org"
124                      and .scan.qr.url.path =~ "/tracker"
125                      and strings.starts_with(.scan.qr.url.query_params,
126                                              "type=dataset&url=http"
127                      )
128                    )
129                    // the QR code contains the email address of a recipient
130                    // allowing for base64 encoded variants
131                    or (
132                      any(filter(recipients.to, .email.domain.valid),
133                          strings.icontains(..scan.qr.url.url, .email.email)
134                          or any(strings.scan_base64(..scan.qr.url.url,
135                                                     ignore_padding=true
136                                 ),
137                                 strings.icontains(., ..email.email)
138                          )
139                          or any(strings.scan_base64(..scan.qr.url.fragment,
140                                                     ignore_padding=true
141                                 ),
142                                 strings.icontains(., ..email.email)
143                          )
144                          or strings.decode_hex(..scan.qr.url.fragment) == .email.email
145                      )
146                    )
147                  )
148                )
149            )
150          )
151  )
152  and (
153    (
154      profile.by_sender_email().prevalence in ("new", "outlier")
155      and not profile.by_sender_email().solicited
156    )
157    or (
158      profile.by_sender_email().any_messages_malicious_or_spam
159      and not profile.by_sender_email().any_messages_benign
160    )
161    or (
162      sender.email.domain.domain in $org_domains
163      and not coalesce(headers.auth_summary.dmarc.pass, false)
164    )
165  )
166  
167  // negate highly trusted sender domains unless they fail DMARC authentication
168  and not (
169    sender.email.domain.root_domain in $high_trust_sender_root_domains
170    and coalesce(headers.auth_summary.dmarc.pass, false)
171  )  
172attack_types:
173  - "Credential Phishing"
174tactics_and_techniques:
175  - "QR code"
176  - "Social engineering"
177detection_methods:
178  - "Computer Vision"
179  - "Header analysis"
180  - "Natural Language Understanding"
181  - "QR code analysis"
182  - "Sender analysis"
183  - "URL analysis"
184  - "URL screenshot"
185id: "9f1681e1-8c15-5edd-9aaa-eb5af1729322"
to-top