Detection rules › Sublime MQL

Brand impersonation: Punchbowl

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

Detects messages impersonating Punchbowl invitations not originating from legitimate Punchbowl domain.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesImpersonation: Brand, Social engineering

Event coverage

Rule body MQL

type.inbound
// Looking for Punchbowl phrasing in all body threads
and (
  strings.icontains(body.current_thread.text, "punchbowl")
  // Look for alt text in HTML for standardized Punchbowl formatting if string is not avail.
  or strings.icontains(body.html.raw, 'alt="Punchbowl"')
  // image sourced from punchbowl
  or any(html.xpath(body.html, '//img/@src').nodes,
         strings.parse_url(.raw).domain.domain == "static.punchbowl.com"
         and strings.icontains(strings.parse_url(.raw).path, '/invitation')
  )
)

// Phrasing is typically "You're invited"
and (
  strings.icontains(body.current_thread.text, "you're invited")
  or any([
           html.xpath(body.html,
                      '//a//img[contains(@src, "btn_open_invitation")]'
           ).nodes,
           html.xpath(body.html,
                      '//a//img[contains(@src, "btn_open_save_the_date")]'
           ).nodes,
         ],
         any(.,
             regex.icontains(.inner_text,
                             '(?:open|save).{0,10}(?:invitation|the date)'
             )
         )
  )
)
// Legitimate sender will be from punchbowl, negating known non-associated domains.
and not sender.email.domain.root_domain in ("punchbowl.com", "punchbowl.news")
// Capping length to limit FP's
and length(body.current_thread.text) < 1500

Detection logic

Scope: inbound message.

Detects messages impersonating Punchbowl invitations not originating from legitimate Punchbowl domain.

  1. inbound message
  2. any of:
    • body.current_thread.text contains 'punchbowl'
    • body.html.raw contains 'alt="Punchbowl"'
    • any of html.xpath(body.html, '//img/@src').nodes where all hold:
      • strings.parse_url(.raw).domain.domain is 'static.punchbowl.com'
      • strings.parse_url(.raw).path contains '/invitation'
  3. any of:
    • body.current_thread.text contains "you're invited"
    • any of [html.xpath(body.html, '//a//img[contains(@src, "btn_open_invitation")]').nodes, html.xpath(body.html, '//a//img[contains(@src, "btn_open_save_the_date")]').nodes] where:
      • any of . where:
        • .inner_text matches '(?:open|save).{0,10}(?:invitation|the date)'
  4. not:
    • sender.email.domain.root_domain in ('punchbowl.com', 'punchbowl.news')
  5. length(body.current_thread.text) < 1500

Inspects: body.current_thread.text, body.html, body.html.raw, sender.email.domain.root_domain, type.inbound. Sensors: html.xpath, regex.icontains, strings.icontains, strings.parse_url.

Indicators matched (7)

FieldMatchValue
strings.icontainssubstringpunchbowl
strings.icontainssubstringalt="Punchbowl"
strings.icontainssubstring/invitation
strings.icontainssubstringyou're invited
regex.icontainsregex(?:open|save).{0,10}(?:invitation|the date)
sender.email.domain.root_domainmemberpunchbowl.com
sender.email.domain.root_domainmemberpunchbowl.news