Service abuse: GitHub notification with excessive mentions and suspicious links
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.
Sublime rule (View on GitHub)
1name: "Service abuse: GitHub notification with excessive mentions and suspicious links"
2description: "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."
3type: "rule"
4severity: "high"
5source: |
6 type.inbound
7 // actual GitHub notifications
8 and sender.email.email == "notifications@github.com"
9 and all(headers.reply_to, .email.domain.domain == "reply.github.com")
10 and headers.return_path.email == "noreply@github.com"
11 // the Message-ID field will contain the unsubscribe link in the body
12 and strings.icontains(headers.message_id,
13 body.links[length(body.links) - 1].href_url.url
14 )
15
16 // negating out-of-scope notification emails from github
17 and not any(recipients.cc,
18 .email.domain.root_domain == "github.com"
19 and .email.local_part in (
20 "assign",
21 "comment",
22 "review_requested",
23 "author",
24 "subscribed",
25 "state_change",
26 "team_mention"
27 )
28 )
29
30 // do not match messages where the sender display name is in the org display names.
31 // This attempts to avoid catching internal employees commenting on org repos
32 and not any($org_display_names, . =~ sender.display_name)
33
34 // there is only a single external link
35 and length(distinct(filter(body.links,
36 // filter any links that go back to github
37 .href_url.domain.root_domain not in (
38 'github.com',
39 'githubusercontent.com',
40 'github.io',
41 'githubsupport.com',
42 'githubstatus.com'
43 )
44 // remove embedded images
45 and not (
46 strings.ends_with(.href_url.url, ".jpg")
47 or strings.ends_with(.href_url.url, "png")
48 or strings.ends_with(.href_url.url, ".svg")
49 or strings.ends_with(.href_url.url, ".gif")
50 )
51 // remove aws codesuite links
52 and not (
53 .href_url.domain.root_domain == "amazon.com"
54 and strings.istarts_with(.href_url.path,
55 '/codesuite/'
56 )
57 )
58 ),
59 .href_url.domain.domain
60 )
61 ) == 1
62
63 // that single link is suspicious
64 and any(
65 // filter any links that go back to github
66 filter(body.links,
67 .href_url.domain.root_domain not in (
68 'github.com',
69 'githubusercontent.com',
70 'github.io',
71 'githubsupport.com',
72 'githubstatus.com'
73 )
74 ),
75 // see if the remaining links are within several lists
76 .href_url.domain.root_domain in $free_file_hosts
77 or (
78 .href_url.domain.root_domain in $free_subdomain_hosts
79 and .href_url.domain.subdomain is not null
80 )
81 or .href_url.domain.root_domain in $url_shorteners
82 // the domain is less than 20 days old
83 or network.whois(.href_url.domain).days_old < 20
84 )
85
86 // The main abuse point is that they will @ multiple people in the github notification
87 and length(filter(body.current_thread.links,
88 strings.starts_with(.display_text, "@")
89 )
90 ) > 20
91attack_types:
92 - "Credential Phishing"
93 - "Malware/Ransomware"
94tactics_and_techniques:
95 - "Free file host"
96 - "Free subdomain host"
97 - "Social engineering"
98detection_methods:
99 - "Header analysis"
100 - "URL analysis"
101 - "Sender analysis"
102 - "Whois"
103id: "4f3a766a-8284-5e89-ad55-9d71ce5eb873"