Brand impersonation: QuickBooks

Impersonation of the Quickbooks service from Intuit.

Sublime rule (View on GitHub)

  1name: "Brand impersonation: QuickBooks"
  2description: "Impersonation of the Quickbooks service from Intuit."
  3type: "rule"
  4severity: "medium"
  5source: |
  6  type.inbound
  7  and (
  8    (
  9      strings.ilike(sender.display_name,
 10                    'quickboo*',
 11                    'QuickB*',
 12                    'QBook*',
 13                    'intuit*'
 14      )
 15      or strings.like(sender.display_name, "QB-*", "QB *")
 16      or regex.icontains(sender.display_name, '\bqb[o]?\b')
 17      or strings.ilevenshtein(sender.display_name, 'quickbooks') <= 1
 18      or strings.ilike(sender.email.domain.domain, '*quickbook*')
 19      or (
 20        length(filter(ml.nlu_classifier(body.current_thread.text).entities,
 21                      strings.icontains(.text, "quickbooks")
 22               )
 23        ) >= 2
 24        and any(ml.nlu_classifier(body.current_thread.text).intents,
 25                .name in ("cred_theft", "callback_scam")
 26        )
 27      )
 28      or (
 29        any(attachments,
 30            length(filter(ml.nlu_classifier(beta.ocr(.).text).entities,
 31                          strings.icontains(.text, "quickbooks")
 32                   )
 33            ) >= 2
 34            and regex.icontains(beta.ocr(.).text,
 35                                '\+?(?:[ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
 36            )
 37        )
 38      )
 39    )
 40    or strings.ilike(body.current_thread.text, "*invoice*")
 41  )
 42  and (
 43    any(ml.logo_detect(file.message_screenshot()).brands,
 44        .name == "Quickbooks" and .confidence in ("medium", "high")
 45    )
 46    or (
 47      any(attachments,
 48          (
 49            any(ml.logo_detect(.).brands,
 50                .name == "Quickbooks" and .confidence in ("medium", "high")
 51            )
 52            // the QuickBooks/Intuit logo lockup, footer or copyright in the
 53            // attachment, mirroring the body-text markers used above
 54            or regex.icontains(beta.ocr(.).text,
 55                               '©\s*(?:[0-9]{4})?\s*intuit\b',
 56                               'intuit\s+(?:inc|quickbooks)\b',
 57                               '@intuit\b',
 58                               'quickbooks\s+(?:billing|support|cloud\s+services|subscription)'
 59            )
 60          )
 61          and any(ml.nlu_classifier(beta.ocr(.).text).intents,
 62                  .name in ("cred_theft", "callback_scam")
 63          )
 64      )
 65    )
 66    // contains the address and copyright
 67    or (
 68      strings.icontains(body.current_thread.text,
 69                        '2800 E. Commerce Center Place, Tucson, AZ 85706',
 70                        '2700 Coast Ave, Mountain View, CA 94043'
 71      )
 72      and regex.icontains(body.current_thread.text, '©\s*(?:\d+)\s*Intuit')
 73    )
 74    or strings.icontains(body.current_thread.text,
 75                         'Powered by QuickBooks',
 76                         'QuickBooks and Intuit are trademarks of Intuit Inc.',
 77                         "QuickBooks Cloud Services",
 78                         "QuickBooks Support Center",
 79                         "QuickBooks subscription"
 80    )
 81    or regex.icontains(body.current_thread.text,
 82                       '(?:Secured by )?QuickBooks Payments'
 83    )
 84    or regex.imatch(body.current_thread.text, '.*quickbooks\s\S+\spayroll')
 85  
 86    // phone number and update language
 87    or (
 88      regex.icontains(body.current_thread.text,
 89                      '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
 90                      '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
 91      )
 92      and any(ml.nlu_classifier(body.current_thread.text).topics,
 93              .name in ("Software and App Updates", "Security and Authentication")
 94      )
 95  
 96      // we need to re-check for QB indicators, otherwise we can have "*invoice*"
 97      // and this block, which is much more than just QB impersonation
 98      and (
 99        strings.ilike(sender.display_name,
100                      'quickbook*',
101                      'QuickB*',
102                      'QBook*',
103                      'intuit*'
104        )
105        or strings.like(sender.display_name, "QB-*", "QB *")
106        or regex.icontains(sender.display_name, '\bqb[o]?\b')
107        or strings.ilevenshtein(sender.display_name, 'quickbooks') <= 1
108        or strings.ilike(sender.email.domain.domain, '*quickbook*')
109        or (
110          length(filter(ml.nlu_classifier(body.current_thread.text).entities,
111                        strings.icontains(.text, "quickbooks")
112                 )
113          ) > 2
114          and any(ml.nlu_classifier(body.current_thread.text).intents,
115                  .name == "cred_theft"
116          )
117        )
118      )
119    )
120    or (
121      sender.email.domain.domain in $free_email_providers
122      and any(headers.domains, .root_domain == "sendgrid.net")
123      and any(ml.nlu_classifier(body.current_thread.text).entities,
124              .name == "org" and .text == "Quickbooks"
125      )
126    )
127    or any(body.links,
128           (
129             regex.icontains(.display_url.url, '(?:quickbooks|intuit)')
130             and .mismatched
131             and not .href_url.domain.root_domain in (
132               "mimecast.com",
133               "mimecastprotect.com"
134             )
135           )
136           or (
137             regex.icontains(.href_url.path, '(?:quickbooks|intuit)')
138             and not strings.icontains(.href_url.domain.root_domain,
139                                       "quickbooks",
140                                       "intuit"
141             )
142             and not any(ml.nlu_classifier(body.current_thread.text).topics,
143                         .name == "Advertising and Promotions"
144             )
145           )
146    )
147    or (
148      regex.icontains(body.current_thread.text, "quickbooks?", 'intuit\b')
149      and any(ml.nlu_classifier(body.current_thread.text).entities,
150              .name == "sender"
151              and (
152                regex.icontains(.text,
153                                'quickbooks?',
154                                'intuit (services?|\bach\b)'
155                )
156                or regex.icontains(.text, '\bqb[o]?\b')
157              )
158      )
159      and any(ml.nlu_classifier(body.current_thread.text).intents,
160              .name == "cred_theft" and .confidence == "high"
161      )
162    )
163  )
164  and not (
165    sender.email.domain.root_domain in~ (
166      'intuit.com',
167      'turbotax.com',
168      'intuit.ca',
169      'meliopayments.com',
170      'qemailserver.com',
171      'intuit.co.uk',
172      'quickbooksonline.com',
173      'tsheets.com'
174    )
175    and coalesce(headers.auth_summary.dmarc.pass, false)
176  )
177  and (
178    not profile.by_sender().any_messages_benign
179    and not profile.by_sender().solicited
180  )
181  // links in body are not known QB domains or the senders root website (both indicative of a legitimate QuickBooks invoice message)
182  and (
183    length(filter(body.links,
184                  .href_url.domain.root_domain in~ (
185                    'intuit.com',
186                    'turbotax.com',
187                    'intuit.ca',
188                    'meliopayments.com',
189                    'qemailserver.com',
190                    'intuit.co.uk',
191                    'quickbooksonline.com'
192                  )
193                  or (
194                    .href_url.domain.root_domain == sender.email.domain.root_domain
195                    and (.href_url.path is null or .href_url.path == "/")
196                  )
197                  // handle links to the root website when the sender uses a freemail address to send invoices
198                  or (
199                    .href_url.domain.sld == sender.email.local_part
200                    and (.href_url.path is null or .href_url.path == "/")
201                    and sender.email.domain.root_domain in $free_email_providers
202                  )
203           )
204    ) != length(body.links)
205    // or no valid links
206    or length(filter(body.links, .href_url.domain.domain is not null)) == 0
207  )
208  // the call to action link does not lead to inuit
209  and not (
210    // filter down to observed call to action display text
211    any(filter(body.links,
212               .display_text in~ (
213                 "view and pay",
214                 "review and pay",
215                 "view details"
216               )
217        ),
218        // benign/legit href_url details for those links
219        (
220          // sendgrid rewritten links
221          .href_url.domain.domain == "links.notification.intuit.com"
222          // CTA link
223          or (
224            .href_url.domain.domain == "connect.intuit.com"
225            and strings.icontains(.href_url.query_params, 'cta=viewinvoicenow')
226          )
227          // Mimecast links
228          or (
229            .href_url.domain.root_domain == "mimecastprotect.com"
230            and (
231              strings.icontains(.href_url.query_params,
232                                'domain=links.notification.intuit.com'
233              )
234              or strings.icontains(.href_url.query_params,
235                                   'domain=connect.intuit.com'
236              )
237            )
238          )
239        )
240    )
241  )
242  // negate common sender of quickbooks reseller
243  and not coalesce(strings.icontains(body.current_thread.text,
244                                     'Purchasing Reviews, Inc'
245                   ),
246                   false
247  )
248  // negate highly trusted sender domains unless they fail DMARC authentication
249  and not (
250    sender.email.domain.root_domain in $high_trust_sender_root_domains
251    and coalesce(headers.auth_summary.dmarc.pass, false)
252  )  
253attack_types:
254  - "Callback Phishing"
255  - "Credential Phishing"
256tactics_and_techniques:
257  - "Impersonation: Brand"
258  - "Social engineering"
259detection_methods:
260  - "Computer Vision"
261  - "Content analysis"
262  - "Header analysis"
263  - "Sender analysis"
264id: "4fd791d1-a053-5c2d-80dd-c6dcdc112a62"
to-top