Detection rules › Sublime MQL

Spam: Commonly observed formatting of unauthorized free giveaways

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

Detects commonly observed formatting of unauthorized giveaways, free tools, and products by multiple different brands.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesImpersonation: Brand, Social engineering

Event coverage

Rule body MQL

type.inbound
and (
  (
    any(html.xpath(body.html, "//div[contains(@style, 'BACKGROUND: URL')]").nodes,
        .raw is not null
    )
  )
  or (
    any(body.links,
        any([
              "blob.core.windows.net",
              "click.email.formula1.com",
              "firmy-praha.eu"
            ],
            ..href_url.domain.domain == .
            or strings.ends_with(..href_url.domain.domain, .)
        )
    )
  )
)
and (
  (
    // subject has # plus random characters only
    regex.icontains(subject.base, "#[a-z0-9]{5,}?")
    // plus one of these
    and (
      // display name has a # + random characters only
      regex.icontains(sender.display_name, "#[a-z0-9]{5,}?")
      // subject starts with a period (yes, both subject cases should be true)
      or strings.starts_with(subject.base, ".")
      // Display name contains at least 2 emojis
      or length(distinct(map(regex.extract(sender.display_name,
                                           '(?P<emoji>[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}])'
                             ),
                             .full_match
                         )
                )
      ) >= 2
    )
  )
  or (
    // Subject contains at least 2 emojias
    length(distinct(map(regex.extract(subject.base,
                                      '(?P<emoji>[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}])'
                        ),
                        .full_match
                    )
           )
    ) >= 2
  )
  or 
  // another variant with different strings that have numbers but the same pattern is in both subject and displayname
  (
    // subject has # plus random characters & numbers
    regex.icontains(subject.base, "#[1-9a-z]+")
    // plus one of these
    and (
      regex.icontains(sender.display_name, "#[1-9a-z]+")
      or strings.icontains(sender.display_name, "rewards")
    )
  )
  or (
    // or prornotions (promotions) once confusables are stripped in subject
    strings.icontains(strings.replace_confusables(subject.base), "prornotions")
    // and rewards in display name
    and strings.icontains(sender.display_name, "rewards")
  )
  or (
    // subject has * plus 4 random characters and numbers *
    regex.icontains(subject.base, '\*[1-9a-z]{4,}\*')
    // same with the display name
    and regex.icontains(sender.display_name, '\*[1-9a-z]{4,}\*')
  )
  or (
    // subject and display name has two *
    strings.count(subject.base, "*") == 2
    and strings.count(sender.display_name, "*") == 2
  )
  or (
    // subject has string of random characters and numbers
    // checking if string has 1 uppercase, 1 lowercase and 1 number
    any(regex.extract(subject.base, '(?:-{1,2}|\s)([a-zA-Z0-9]{11,})'),
        regex.contains(.full_match, '[A-Z]')
        and regex.contains(.full_match, '[a-z]')
        and regex.contains(.full_match, '[0-9]')
        // some matches are legit but they are 35+ characters
        and length(.full_match) <= 30
    )
    // negating support thread email subjects containg multiple : in their IDs
    and not regex.count(subject.base, ':') > 5
  )
)

Detection logic

Scope: inbound message.

Detects commonly observed formatting of unauthorized giveaways, free tools, and products by multiple different brands.

  1. inbound message
  2. any of:
    • any of html.xpath(body.html, "//div[contains(@style, 'BACKGROUND: URL')]").nodes where:
      • .raw is set
    • any of body.links where:
      • any of ['blob.core.windows.net', 'click.email.formula1.com', 'firmy-praha.eu'] where any holds:
        • .href_url.domain.domain is .
        • strings.ends_with(.href_url.domain.domain)
  3. any of:
    • all of:
      • subject.base matches '#[a-z0-9]{5,}?'
      • any of:
        • sender.display_name matches '#[a-z0-9]{5,}?'
        • subject.base starts with '.'
        • length(distinct(map(regex.extract(sender.display_name, '(?P<emoji>[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}])'), .full_match))) ≥ 2
    • length(distinct(map(regex.extract(subject.base, '(?P<emoji>[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}])'), .full_match))) ≥ 2
    • all of:
      • subject.base matches '#[1-9a-z]+'
      • any of:
        • sender.display_name matches '#[1-9a-z]+'
        • sender.display_name contains 'rewards'
    • all of:
      • strings.replace_confusables(subject.base) contains 'prornotions'
      • sender.display_name contains 'rewards'
    • all of:
      • subject.base matches '\\*[1-9a-z]{4,}\\*'
      • sender.display_name matches '\\*[1-9a-z]{4,}\\*'
    • all of:
      • strings.count(subject.base, '*') is 2
      • strings.count(sender.display_name, '*') is 2
    • all of:
      • any of regex.extract(subject.base) where all hold:
        • .full_match matches '[A-Z]'
        • .full_match matches '[a-z]'
        • .full_match matches '[0-9]'
        • length(.full_match) ≤ 30
      • not:
        • regex.count(subject.base, ':') > 5

Inspects: body.html, body.links, body.links[].href_url.domain.domain, sender.display_name, subject.base, type.inbound. Sensors: html.xpath, regex.contains, regex.count, regex.extract, regex.icontains, strings.count, strings.ends_with, strings.icontains, strings.replace_confusables, strings.starts_with.

Indicators matched (12)

FieldMatchValue
regex.icontainsregex#[a-z0-9]{5,}?
strings.starts_withprefix.
regex.extractregex(?P<emoji>[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}])
regex.icontainsregex#[1-9a-z]+
strings.icontainssubstringrewards
strings.icontainssubstringprornotions
regex.icontainsregex\*[1-9a-z]{4,}\*
regex.extractregex(?:-{1,2}|\s)([a-zA-Z0-9]{11,})
regex.containsregex[A-Z]
regex.containsregex[a-z]
regex.containsregex[0-9]
regex.countregex: