Service abuse: Sendgrid credential theft with personalized request targeting single recipient

Detects messages sent through Sendgrid from new sender domains that contain credential theft language with high confidence. The message targets a single recipient whose email address appears in both the message body and link display text, indicating personalization tactics commonly used in targeted attacks.

Sublime rule (View on GitHub)

 1name: "Service abuse: Sendgrid credential theft with personalized request targeting single recipient"
 2description: "Detects messages sent through Sendgrid from new sender domains that contain credential theft language with high confidence. The message targets a single recipient whose email address appears in both the message body and link display text, indicating personalization tactics commonly used in targeted attacks."
 3type: "rule"
 4severity: "medium"
 5source: |
 6  type.inbound
 7  // a single recipient
 8  and length(recipients.to) == 1
 9  // the domain is a first time sender
10  and profile.by_sender_domain().prevalence == "new"
11  // sent from sendgrid
12  and any(headers.domains, .root_domain == "sendgrid.net")
13  // cred_theft intent
14  and any(ml.nlu_classifier(body.current_thread.text).intents,
15          .name == "cred_theft" and .confidence != "low"
16  )
17  // a request is within the display_text
18  and any(filter(ml.nlu_classifier(body.current_thread.text).entities,
19                 .name == "request"
20          ),
21          any(body.links, .display_text == ..text)
22  )
23  // the rcpt email address is in the body of the message, accounting for display_url, which also might include it
24  and (
25    // number of occurances the rcpt email occurs in the body
26    strings.count(body.current_thread.text, recipients.to[0].email.email) > 
27    // length of the filtered links to those that contain the email
28    length(filter(body.links,
29                  strings.contains(.display_url.url, recipients.to[0].email.email)
30           )
31    )
32  )
33  and not (
34    strings.icontains(body.current_thread.text,
35                      strings.concat('This message was generated automatically for ',
36                                     recipients.to[0].email.email
37                      )
38    )
39    or strings.icontains(body.current_thread.text,
40                      strings.concat('This email was sent to ',
41                                     recipients.to[0].email.email
42                      )
43    )
44  )  
45
46attack_types:
47  - "Credential Phishing"
48tactics_and_techniques:
49  - "Social engineering"
50detection_methods:
51  - "Header analysis"
52  - "Natural Language Understanding"
53  - "Sender analysis"
54  - "Content analysis"
55id: "b9680da1-5fc8-5c11-9fd0-a2789a589bac"
to-top