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        or any(filter(attachments, .file_extension in ('pdf', 'docx')),
 32               any(recipients.to,
 33                   strings.icontains(..file_name, .email.domain.sld)
 34               )
 35        )
 36      )
 37    )
 38  )
 39  
 40  // Inspects image attachments for QR codes
 41  and any(attachments,
 42          (
 43            .file_type in $file_types_images
 44            or .file_type == "pdf"
 45            or .file_extension in $file_extensions_macros
 46          )
 47          and (
 48            any(file.explode(.),
 49                .scan.qr.type == "url"
 50                and not .scan.qr.url.domain.domain == "geico.app.link"
 51                and (
 52                  // pass the QR URL to LinkAnalysis
 53                  any([ml.link_analysis(.scan.qr.url)],
 54                      .credphish.disposition == "phishing"
 55  
 56                      // any routing traverses via $suspicious_tld list
 57                      or any(.redirect_history, .domain.tld in $suspicious_tlds)
 58  
 59                      // effective destination in $suspicious_tld list
 60                      or .effective_url.domain.tld in $suspicious_tlds
 61  
 62                      // or the effective destination domain is in $abuse_ch_urlhaus_domains_trusted_reporters
 63                      or .effective_url.domain.root_domain in $abuse_ch_urlhaus_domains_trusted_reporters
 64  
 65                      // or any files downloaded are zips or executables
 66                      or any(.files_downloaded,
 67                             .file_extension in $file_extensions_common_archives
 68                             or .file_extension in $file_extensions_executables
 69                      )
 70                  )
 71                  or (
 72  
 73                    // or the QR code's root domain is a url_shortener
 74                    .scan.qr.url.domain.root_domain in $url_shorteners
 75                    or .scan.qr.url.domain.root_domain in $social_landing_hosts
 76                    and (
 77                      not (
 78                        any(ml.nlu_classifier(body.current_thread.text).intents,
 79                            .name == "benign"
 80                        )
 81                        or any(ml.nlu_classifier(body.current_thread.text).entities,
 82                               .name == "disclaimer"
 83                        )
 84                      )
 85                      or not any(attachments,
 86                                 any(file.explode(.),
 87                                     any(ml.nlu_classifier(.scan.ocr.raw).intents,
 88                                         .name == "benign"
 89                                     )
 90                                 )
 91                      )
 92                      or (
 93                        any(recipients.to,
 94                            strings.icontains(..scan.qr.url.url, .email.email)
 95                        )
 96                      )
 97                    )
 98  
 99                    // exclude google maps
100                    and not strings.starts_with(.scan.qr.url.url,
101                                                'https://goo.gl/maps'
102                    )
103                    and not strings.starts_with(.scan.qr.url.url,
104                                                'https://maps.app.goo.gl'
105                    )
106                  )
107  
108                  // the QR code url is a bing open redirect
109                  or (
110                    .scan.qr.url.domain.root_domain == 'bing.com'
111                    and .scan.qr.url.path =~ '/ck/a'
112                  )
113                  // QR code contains non ascii chars
114                  or regex.contains(.scan.qr.url.url, '[^\x00-\x7F]')
115                  or (
116  
117                    // usap-dc open redirect
118                    .scan.qr.url.domain.root_domain == "usap-dc.org"
119                    and .scan.qr.url.path =~ "/tracker"
120                    and strings.starts_with(.scan.qr.url.query_params,
121                                            "type=dataset&url=http"
122                    )
123                    or (
124                      any(recipients.to,
125                          strings.icontains(..scan.qr.url.url, .email.email)
126                          or any(beta.scan_base64(..scan.qr.url.url,
127                                                  ignore_padding=true
128                                 ),
129                                 strings.icontains(., ..email.email)
130                          )
131                          or any(beta.scan_base64(..scan.qr.url.fragment,
132                                                  ignore_padding=true
133                                 ),
134                                 strings.icontains(., ..email.email)
135                          )
136                      )
137                    )
138                  )
139                )
140            )
141          )
142  )
143  and (
144    (
145      profile.by_sender_email().prevalence in ("new", "outlier")
146      and not profile.by_sender_email().solicited
147    )
148    or (
149      profile.by_sender_email().any_messages_malicious_or_spam
150      and not profile.by_sender_email().any_messages_benign
151    )
152    or (
153        sender.email.domain.domain in $org_domains
154        and not coalesce(headers.auth_summary.dmarc.pass, false)
155    )
156  )
157  
158  // negate highly trusted sender domains unless they fail DMARC authentication
159  and (
160    (
161      sender.email.domain.root_domain in $high_trust_sender_root_domains
162      and not headers.auth_summary.dmarc.pass
163    )
164    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
165  )  
166attack_types:
167  - "Credential Phishing"
168tactics_and_techniques:
169  - "QR code"
170  - "Social engineering"
171detection_methods:
172  - "Computer Vision"
173  - "Header analysis"
174  - "Natural Language Understanding"
175  - "QR code analysis"
176  - "Sender analysis"
177  - "URL analysis"
178  - "URL screenshot"
179id: "9f1681e1-8c15-5edd-9aaa-eb5af1729322"
to-top