Detection rules › Sublime MQL

Spam: Website errors solicitation

Severity
low
Type
rule
Source
github.com/sublime-security/sublime-rules

This rule detects messages claiming to have identified errors on a website. The messages typically offer to send pricing or information upon request.

Threat classification

Sublime's own taxonomy (not MITRE ATT&CK).

CategoryValues
Attack typesSpam

Event coverage

Rule body MQL

type.inbound
and not profile.by_sender().solicited
// no attachments
and length(attachments) == 0
// subject must contain SEO or web dev spam keywords or be short
and (
  (
    // SEO or web development service keywords
    regex.icontains(strings.replace_confusables(subject.subject),
                    '(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)'
    )
    or regex.icontains(subject.base,
                       '[^\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}][\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}]\x{FE0F}?$'
    )
    // report and follow up keywords
    or (
      strings.icontains(strings.replace_confusables(subject.subject), "report")
      and regex.icontains(strings.replace_confusables(body.current_thread.text),
                          "(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)"
      )
    )
    // short subject
    or length(subject.base) < 7
  )
  // or a reply or forward in a thread that mentions website or screenshots
  or (
    (length(subject.base) < 5 or subject.is_reply or subject.is_forward)
    and any(body.previous_threads,
            regex.icontains(strings.replace_confusables(.text),
                            "(?:screenshot|website)"
            )
    )
  )
)
// body structure and content patterns
and (
  // Single thread with no links
  (
    length(filter(body.current_thread.links,
                  not (.href_url.scheme == "mailto" and .parser == "plain")
           )
    ) == 0
    and length(body.previous_threads) == 0
    // short message between 20 and 500 chars
    and (
      20 < length(body.current_thread.text) < 500
      or any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
                        .name == "disclaimer"
                 ),
                 .text
             ),
             20 < (length(body.current_thread.text) - length(.)) < 500
      )
    )
    // service offering keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details)"
    )
    // generic greeting
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        'h(?:i|ello|ey)\b'
    )
    // problem or urgency keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:errors?|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|glitch|send you|SEO|broken)'
    )
    // website or page mention
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:site|website|page|package|SEO)"
    )
  )
  // Single thread with unsubscribe link or $org_domains link
  or (
    length(body.links) <= 3
    and (
      // unsubscribe mailto link
      regex.icontains(body.html.raw, "mailto:*[++unsubscribe@]")
      // or link to found in org_domains
      or any(body.links, .href_url.domain.root_domain in~ $org_domains)
    )
    and length(body.previous_threads) == 0
    // short message between 20 and 500 chars
    and 20 < length(body.current_thread.text) < 500
    // service offering keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:screenshot|error list|plan|quote|rank|professional|price|mistake)"
    )
    // generic greeting
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:h(?:i|ello|ey)|morning)\b'
    )
    // problem or urgency keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:error|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|send you|SEO)'
    )
    // website or page mention
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:site|website|page|package|SEO)"
    )
  )
  // Multiple thread messages
  or (
    length(body.links) == 0
    // small thread with less than 5 messages
    and length(body.previous_threads) < 5
    // check previous messages for spam characteristics
    and any(body.previous_threads,
            // short previous messages less than 400 chars
            length(.text) < 400
            and (
              // generic greeting
              regex.icontains(strings.replace_confusables(.text),
                              '(?:h(?:i|ello|ey)|morning)\b'
              )
              // service offering keywords
              and regex.icontains(strings.replace_confusables(.text),
                                  '(?:\berror(?:\s+list)?\b|screenshot|report|plan)'
              )
              // previous threads written in English
              and ml.nlu_classifier(.text).language == "english"
            )
    )
  )
)

Detection logic

Scope: inbound message.

This rule detects messages claiming to have identified errors on a website. The messages typically offer to send pricing or information upon request.

  1. inbound message
  2. not:
    • profile.by_sender().solicited
  3. length(attachments) is 0
  4. any of:
    • any of:
      • strings.replace_confusables(subject.subject) matches '(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)'
      • subject.base matches '[^\\x{2600}-\\x{27BF}\\x{1F300}-\\x{1F9FF}][\\x{2600}-\\x{27BF}\\x{1F300}-\\x{1F9FF}]\\x{FE0F}?$'
      • all of:
        • strings.replace_confusables(subject.subject) contains 'report'
        • strings.replace_confusables(body.current_thread.text) matches '(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)'
      • length(subject.base) < 7
    • all of:
      • any of:
        • length(subject.base) < 5
        • subject.is_reply
        • subject.is_forward
      • any of body.previous_threads where:
        • strings.replace_confusables(.text) matches '(?:screenshot|website)'
  5. any of:
    • all of:
      • length(filter(body.current_thread.links, not .href_url.scheme == 'mailto' and .parser == 'plain')) is 0
      • length(body.previous_threads) is 0
      • any of:
        • all of:
          • length(body.current_thread.text) > 20
          • length(body.current_thread.text) < 500
        • any of map(...) where all hold:
          • 20 < length(body.current_thread.text) - length(.)
          • length(body.current_thread.text) - length(.) < 500
      • strings.replace_confusables(body.current_thread.text) matches '(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details)'
      • strings.replace_confusables(body.current_thread.text) matches 'h(?:i|ello|ey)\\b'
      • strings.replace_confusables(body.current_thread.text) matches '(?:errors?|report|issues|website|repair|redesign|upgrade|Google\\s+.{0,15}find it|glitch|send you|SEO|broken)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:site|website|page|package|SEO)'
    • all of:
      • length(body.links) ≤ 3
      • any of:
        • body.html.raw matches 'mailto:*[++unsubscribe@]'
        • any of body.links where:
          • .href_url.domain.root_domain in $org_domains
      • length(body.previous_threads) is 0
      • all of:
        • length(body.current_thread.text) > 20
        • length(body.current_thread.text) < 500
      • strings.replace_confusables(body.current_thread.text) matches '(?:screenshot|error list|plan|quote|rank|professional|price|mistake)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:h(?:i|ello|ey)|morning)\\b'
      • strings.replace_confusables(body.current_thread.text) matches '(?:error|report|issues|website|repair|redesign|upgrade|Google\\s+.{0,15}find it|send you|SEO)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:site|website|page|package|SEO)'
    • all of:
      • length(body.links) is 0
      • length(body.previous_threads) < 5
      • any of body.previous_threads where all hold:
        • length(.text) < 400
        • all of:
          • strings.replace_confusables(.text) matches '(?:h(?:i|ello|ey)|morning)\\b'
          • strings.replace_confusables(.text) matches '(?:\\berror(?:\\s+list)?\\b|screenshot|report|plan)'
          • ml.nlu_classifier(.text).language is 'english'

Inspects: body.current_thread.links, body.current_thread.links[].href_url.scheme, body.current_thread.links[].parser, body.current_thread.text, body.html.raw, body.links, body.links[].href_url.domain.root_domain, body.previous_threads, body.previous_threads[].text, subject.base, subject.is_forward, subject.is_reply, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, strings.icontains, strings.replace_confusables. Reference lists: $org_domains.

Indicators matched (17)

FieldMatchValue
regex.icontainsregex(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)
regex.icontainsregex[^\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}][\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}]\x{FE0F}?$
strings.icontainssubstringreport
regex.icontainsregex(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)
regex.icontainsregex(?:screenshot|website)
body.current_thread.links[].href_url.schemeequalsmailto
body.current_thread.links[].parserequalsplain
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsdisclaimer
regex.icontainsregex(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details)
regex.icontainsregexh(?:i|ello|ey)\b
regex.icontainsregex(?:errors?|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|glitch|send you|SEO|broken)
regex.icontainsregex(?:site|website|page|package|SEO)
5 more
regex.icontainsregexmailto:*[++unsubscribe@]
regex.icontainsregex(?:screenshot|error list|plan|quote|rank|professional|price|mistake)
regex.icontainsregex(?:h(?:i|ello|ey)|morning)\b
regex.icontainsregex(?:error|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|send you|SEO)
regex.icontainsregex(?:\berror(?:\s+list)?\b|screenshot|report|plan)