Detection rules › Sublime MQL

Service abuse: GitHub notification with excessive mentions and suspicious links

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

Detects messages impersonating GitHub notifications that contain excessive @ mentions (over 20) and include a single suspicious external link. The suspicious link may be from free file hosts, free subdomain hosts, URL shorteners, or newly registered domains. The rule filters out legitimate GitHub domains and internal employee communications while identifying potential abuse of GitHub's notification system.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Malware/Ransomware
Tactics and techniquesFree file host, Free subdomain host, Social engineering

Event coverage

Rule body MQL

type.inbound
// actual GitHub notifications
and sender.email.email == "notifications@github.com"
and all(headers.reply_to, .email.domain.domain == "reply.github.com")
and headers.return_path.email == "noreply@github.com"
// the Message-ID field will contain the unsubscribe link in the body
and strings.icontains(headers.message_id,
                      body.links[length(body.links) - 1].href_url.url
)

// negating out-of-scope notification emails from github
and not any(recipients.cc,
            .email.domain.root_domain == "github.com"
            and .email.local_part in (
              "assign",
              "comment",
              "review_requested",
              "author",
              "subscribed",
              "state_change",
              "team_mention"
            )
)

// do not match messages where the sender display name is in the org display names.
// This attempts to avoid catching internal employees commenting on org repos
and not any($org_display_names, . =~ sender.display_name)

// there is only a single external link
and length(distinct(filter(body.links,
                           // filter any links that go back to github
                           .href_url.domain.root_domain not in (
                             'github.com',
                             'githubusercontent.com',
                             'github.io',
                             'githubsupport.com',
                             'githubstatus.com'
                           )
                           // remove embedded images
                           and not (
                             strings.ends_with(.href_url.url, ".jpg")
                             or strings.ends_with(.href_url.url, "png")
                             or strings.ends_with(.href_url.url, ".svg")
                             or strings.ends_with(.href_url.url, ".gif")
                           )
                           // remove aws codesuite links
                           and not (
                             .href_url.domain.root_domain == "amazon.com"
                             and strings.istarts_with(.href_url.path,
                                                      '/codesuite/'
                             )
                           )
                    ),
                    .href_url.domain.domain
           )
) == 1

// that single link is suspicious
and any(
        // filter any links that go back to github
        filter(body.links,
               .href_url.domain.root_domain not in (
                 'github.com',
                 'githubusercontent.com',
                 'github.io',
                 'githubsupport.com',
                 'githubstatus.com'
               )
        ),
        // see if the remaining links are within several lists
        .href_url.domain.root_domain in $free_file_hosts
        or (
          .href_url.domain.root_domain in $free_subdomain_hosts
          and .href_url.domain.subdomain is not null
        )
        or .href_url.domain.root_domain in $url_shorteners
        // the domain is less than 20 days old
        or network.whois(.href_url.domain).days_old < 20
)

// The main abuse point is that they will @ multiple people in the github notification
and length(filter(body.current_thread.links,
                  strings.starts_with(.display_text, "@")
           )
) > 20

Detection logic

Scope: inbound message.

Detects messages impersonating GitHub notifications that contain excessive @ mentions (over 20) and include a single suspicious external link. The suspicious link may be from free file hosts, free subdomain hosts, URL shorteners, or newly registered domains. The rule filters out legitimate GitHub domains and internal employee communications while identifying potential abuse of GitHub's notification system.

  1. inbound message
  2. sender.email.email is 'notifications@github.com'
  3. all of headers.reply_to where:
    • .email.domain.domain is 'reply.github.com'
  4. headers.return_path.email is 'noreply@github.com'
  5. strings.icontains(headers.message_id)
  6. not:
    • any of recipients.cc where all hold:
      • .email.domain.root_domain is 'github.com'
      • .email.local_part in ('assign', 'comment', 'review_requested', 'author', 'subscribed', 'state_change', 'team_mention')
  7. not:
    • any of $org_display_names where:
      • . is sender.display_name
  8. length(distinct(filter(body.links, .href_url.domain.root_domain not in ('github.com', 'githubusercontent.com', 'github.io', 'githubsupport.com', 'githubstatus.com') and not strings.ends_with(.href_url.url, '.jpg') or strings.ends_with(.href_url.url, 'png') or strings.ends_with(.href_url.url, '.svg') or strings.ends_with(.href_url.url, '.gif') and not .href_url.domain.root_domain == 'amazon.com' and strings.istarts_with(.href_url.path, '/codesuite/')), .href_url.domain.domain)) is 1
  9. any of filter(body.links) where any holds:
    • .href_url.domain.root_domain in $free_file_hosts
    • all of:
      • .href_url.domain.root_domain in $free_subdomain_hosts
      • .href_url.domain.subdomain is set
    • .href_url.domain.root_domain in $url_shorteners
    • network.whois(.href_url.domain).days_old < 20
  10. length(filter(body.current_thread.links, strings.starts_with(.display_text, '@'))) > 20

Inspects: body.current_thread.links, body.current_thread.links[].display_text, body.links, body.links[].href_url.domain.root_domain, body.links[].href_url.path, body.links[].href_url.url, headers.message_id, headers.reply_to, headers.reply_to[].email.domain.domain, headers.return_path.email, recipients.cc, recipients.cc[].email.domain.root_domain, recipients.cc[].email.local_part, sender.display_name, sender.email.email, type.inbound. Sensors: network.whois, strings.ends_with, strings.icontains, strings.istarts_with, strings.starts_with. Reference lists: $free_file_hosts, $free_subdomain_hosts, $org_display_names, $url_shorteners.

Indicators matched (23)

FieldMatchValue
sender.email.emailequalsnotifications@github.com
headers.reply_to[].email.domain.domainequalsreply.github.com
headers.return_path.emailequalsnoreply@github.com
recipients.cc[].email.domain.root_domainequalsgithub.com
recipients.cc[].email.local_partmemberassign
recipients.cc[].email.local_partmembercomment
recipients.cc[].email.local_partmemberreview_requested
recipients.cc[].email.local_partmemberauthor
recipients.cc[].email.local_partmembersubscribed
recipients.cc[].email.local_partmemberstate_change
recipients.cc[].email.local_partmemberteam_mention
body.links[].href_url.domain.root_domainmembergithub.com
11 more
body.links[].href_url.domain.root_domainmembergithubusercontent.com
body.links[].href_url.domain.root_domainmembergithub.io
body.links[].href_url.domain.root_domainmembergithubsupport.com
body.links[].href_url.domain.root_domainmembergithubstatus.com
strings.ends_withsuffix.jpg
strings.ends_withsuffixpng
strings.ends_withsuffix.svg
strings.ends_withsuffix.gif
body.links[].href_url.domain.root_domainequalsamazon.com
strings.istarts_withprefix/codesuite/
strings.starts_withprefix@