Spam: Fake photo share

Message contains pretexting language about sharing photos ("found these photos and thought you'd like them", "remember these photos?") and a link with a newly registered domain. Fake threads and plain text bodies have been seen in the wild, indicating active evasion techniques.

Sublime rule (View on GitHub)

  1name: "Spam: Fake photo share"
  2description: 'Message contains pretexting language about sharing photos ("found these photos and thought you''d like them", "remember these photos?") and a link with a newly registered domain. Fake threads and plain text bodies have been seen in the wild, indicating active evasion techniques.'
  3type: "rule"
  4severity: "low"
  5source: |
  6  type.inbound
  7  and length(attachments) == 0
  8  and (
  9    (
 10      (
 11        (
 12          (length(body.plain.raw) < 500 and length(body.current_thread.text) == 0)
 13          or (
 14            length(body.html.display_text) < 500
 15            and length(body.current_thread.text) == 0
 16          )
 17          or length(body.current_thread.text) < 500
 18          or any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
 19                            .name == "disclaimer"
 20                     ),
 21                     .text
 22                 ),
 23                 (length(body.current_thread.text) - length(.)) < 500
 24          )
 25        )
 26        and (
 27          strings.ilike(subject.subject,
 28                        "*picture*",
 29                        "*photo*",
 30                        "*image*",
 31                        "*sad news*",
 32                        "*sad announcement*",
 33                        "*sad update*",
 34                        "*new pics*",
 35                        "*Reunion*",
 36                        "*planing*",
 37                        "*back memories*",
 38                        "*any memories*",
 39                        "*old memories*",
 40                        "*evoke memories*",
 41                        "*bittersweet memories*"
 42          )
 43          or regex.icontains(subject.base, 'pics?\s*\(\d{1,2}\)')
 44        )
 45      )
 46      or (
 47        (
 48          (
 49            length(body.html.display_text) < 500
 50            and length(body.current_thread.text) == 0
 51          )
 52          and strings.ilike(body.html.display_text,
 53                            "*picture*",
 54                            "*photo*",
 55                            "*image*",
 56                            "*sad news*",
 57                            "*sad announcement*",
 58                            "*sad update*",
 59                            "*new pics*",
 60                            "*back memories*",
 61                            "*any memories*",
 62                            "*old memories*",
 63                            "*evoke memories*",
 64                            "*bittersweet memories*"
 65          )
 66        )
 67        or (
 68          (length(body.plain.raw) < 500 and length(body.current_thread.text) == 0)
 69          and strings.ilike(body.plain.raw,
 70                            "*picture*",
 71                            "*photo*",
 72                            "*image*",
 73                            "*sad news*",
 74                            "*sad announcement*",
 75                            "*sad update*",
 76                            "*new pics*",
 77                            "*back memories*",
 78                            "*any memories*",
 79                            "*old memories*",
 80                            "*evoke memories*",
 81                            "*bittersweet memories*"
 82          )
 83          and not strings.icontains(body.plain.raw, "[cid:image")
 84        )
 85        or (
 86          length(body.current_thread.text) < 500
 87          and strings.ilike(body.current_thread.text,
 88                            "*picture*",
 89                            "*photo*",
 90                            "*image*",
 91                            "*sad news*",
 92                            "*sad announcement*",
 93                            "*sad update*",
 94                            "*new pics*",
 95                            "*back memories*",
 96                            "*any memories*",
 97                            "*old memories*",
 98                            "*evoke memories*",
 99                            "*bittersweet memories*"
100          )
101        )
102      )
103      or (
104        body.plain.raw is not null
105        and body.html.display_text is null
106        and (
107          length(body.current_thread.text) == 0
108          or (
109            length(body.current_thread.text) < 500
110            // fake forward indicator in the plain text body
111            and (
112              regex.contains(body.plain.raw,
113                             'On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote'
114              )
115              or strings.icontains(body.plain.raw, 'Original Message')
116            )
117            and not regex.contains(body.current_thread.text,
118                                   'On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote'
119            )
120            and not strings.icontains(body.current_thread.text,
121                                      'Original Message'
122            )
123            and not any(headers.hops, any(.fields, .name == "Resent-From"))
124          )
125        )
126        and strings.ilike(body.plain.raw,
127                          "*picture*",
128                          "*photo*",
129                          "*image*",
130                          "*sad news*",
131                          "*sad announcement*",
132                          "*sad update*",
133                          "*pics*",
134                          "*back memories*",
135                          "*any memories*",
136                          "*old memories*",
137                          "*evoke memories*",
138                          "*bittersweet memories*"
139        )
140        and not strings.istarts_with(body.plain.raw, "[cid:image")
141        and strings.icontains(subject.subject, sender.display_name)
142      )
143      or (
144        strings.icontains(subject.subject, sender.display_name)
145        and sender.email.domain.root_domain in $free_email_providers
146        and length(body.links) == 2
147        and length(filter(body.links, .display_text == "h")) == 1
148        and length(filter(body.links, .display_url.scheme == "ttp")) == 1
149      )
150    )
151    and length(body.links) < 5
152    and any(body.links,
153            (
154              (
155                network.whois(.href_url.domain).days_old < 30
156                or network.whois(.display_url.domain).days_old < 30
157                or not network.whois(.href_url.domain).found
158                or network.whois(.href_url.domain).found is null
159              )
160              and .href_url.domain.root_domain != sender.email.domain.root_domain
161            )
162            or (
163              length(.display_text) == 1
164              and .href_url.domain.root_domain in ("facebook.com", "youtube.com")
165            )
166            or (
167              // random 5-6 character subdomain
168              regex.icontains(.href_url.domain.domain,
169                              '^[a-z]{5,6}\.[a-z]{5,}\.[a-z]+'
170              )
171              // subdomain contains 3+ consecutive consonants
172              and regex.icontains(.href_url.domain.domain,
173                                  '^[a-z]*[b-df-hj-np-tv-z]{3,}[a-z]*\.'
174              )
175              and network.whois(.href_url.domain).days_old < 365
176            )
177    )
178  )
179  and (
180    (
181      (length(headers.references) > 0 or headers.in_reply_to is null)
182      and not (
183        (
184          strings.istarts_with(subject.subject, "RE:")
185          or strings.istarts_with(subject.subject, "R:")
186          or strings.istarts_with(subject.subject, "ODG:")
187          or strings.istarts_with(subject.subject, "答复:")
188          or strings.istarts_with(subject.subject, "AW:")
189          or strings.istarts_with(subject.subject, "TR:")
190          or strings.istarts_with(subject.subject, "FWD:")
191          or regex.imatch(subject.subject, '(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:')
192        )
193      )
194    )
195    or length(headers.references) == 0
196  )
197  // negate highly trusted sender domains unless they fail DMARC authentication
198  and (
199    (
200      sender.email.domain.root_domain in $high_trust_sender_root_domains
201      and not headers.auth_summary.dmarc.pass
202    )
203    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
204  )  
205attack_types:
206  - "Spam"
207tactics_and_techniques:
208  - "Evasion"
209  - "Social engineering"
210detection_methods:
211  - "Content analysis"
212  - "Sender analysis"
213  - "URL analysis"
214  - "Whois"
215id: "eb086f7d-3ad7-52cd-8e16-3ce08726b9ea"
to-top