GitHub Activity on a Private Repository from an Unusual ASN
Detects when a GitHub user pushes to or clones a private repository from an autonomous system (ASN) not previously seen for that user in the last 7 days. The source IP is resolved to its ASN at query time, so address churn inside the same ISP or cloud provider does not produce new alerts. Adversaries may access private repositories from unfamiliar ASNs to exfiltrate sensitive code or data, potentially indicating a compromise or unauthorized access.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/12/16"
3integration = ["github"]
4maturity = "production"
5min_stack_comments = "ES|QL IP_LOCATION requires 9.5.0+."
6min_stack_version = "9.5.0"
7updated_date = "2026/10/07"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects when a GitHub user pushes to or clones a private repository from an autonomous system (ASN) not previously
13seen for that user in the last 7 days. The source IP is resolved to its ASN at query time, so address churn inside the
14same ISP or cloud provider does not produce new alerts. Adversaries may access private repositories from unfamiliar
15ASNs to exfiltrate sensitive code or data, potentially indicating a compromise or unauthorized access.
16"""
17false_positives = [
18 """
19 Developers travelling or switching between home, mobile and corporate networks will appear from a new ASN the first
20 time each is used. CI runners and cloud build services appear once per cloud provider ASN; add exceptions for known
21 automation identities if the volume is high.
22 """,
23]
24from = "now-10085m"
25interval = "5m"
26language = "esql"
27license = "Elastic License v2"
28name = "GitHub Activity on a Private Repository from an Unusual ASN"
29references = [
30 "https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack",
31 "https://trigger.dev/blog/shai-hulud-postmortem",
32 "https://posthog.com/blog/nov-24-shai-hulud-attack-post-mortem",
33]
34risk_score = 21
35rule_id = "daf2e0e0-0bab-4672-bfa1-62db0ee5ec22"
36severity = "low"
37tags = [
38 "Domain: Cloud",
39 "Use Case: Threat Detection",
40 "Tactic: Impact",
41 "Tactic: Initial Access",
42 "Tactic: Persistence",
43 "Data Source: Github",
44 "Data Source: GitHub Audit Logs",
45 "Resources: Investigation Guide",
46 "Noise: High",
47 "Performance: Fast",
48 "Profile: Aggressive",
49 "Threat: Supply Chain",
50 "Rule Type: ES|QL",
51 "Platform: GitHub",
52 "Domain: SaaS",
53]
54timestamp_override = "event.ingested"
55type = "esql"
56query = '''
57FROM logs-github.audit-*
58| WHERE event.dataset == "github.audit" AND event.action IN ("git.push", "git.clone")
59 AND github.repository_public == false
60 AND user.name IS NOT NULL AND github.actor_ip IS NOT NULL
61| IP_LOCATION asn = github.actor_ip WITH { "database_file": "GeoLite2-ASN.mmdb", "properties": ["asn", "organization_name"] }
62| IP_LOCATION geo = github.actor_ip WITH { "properties": ["country_iso_code"] }
63| WHERE asn.asn IS NOT NULL
64| STATS Esql.event_count = COUNT(*),
65 Esql.first_seen = MIN(@timestamp),
66 Esql.last_seen = MAX(@timestamp),
67 Esql.ip_count = COUNT_DISTINCT(github.actor_ip),
68 Esql.ip_values = VALUES(github.actor_ip),
69 Esql.repo_values = VALUES(github.repo),
70 Esql.event_action_values = VALUES(event.action),
71 Esql.country_values = VALUES(geo.country_iso_code),
72 Esql.user_agent_values = VALUES(github.user_agent)
73 BY user.name, asn.asn, asn.organization_name
74// The 7-day history is the rule look-back (from = now-10085m). A user/ASN pair is new when its first
75// event falls inside the current interval; the 10m bound allows for the 5m interval plus scheduling drift.
76| WHERE DATE_DIFF("minute", Esql.first_seen, NOW()) <= 10
77| EVAL Esql.source_as_number = asn.asn, Esql.source_as_organization_name = asn.organization_name
78| KEEP user.name, Esql.*
79'''
80
81[[rule.threat]]
82framework = "MITRE ATT&CK"
83
84[rule.threat.tactic]
85id = "TA0040"
86name = "Impact"
87reference = "https://attack.mitre.org/tactics/TA0040/"
88
89[[rule.threat]]
90framework = "MITRE ATT&CK"
91
92[[rule.threat.technique]]
93id = "T1078"
94name = "Valid Accounts"
95reference = "https://attack.mitre.org/techniques/T1078/"
96
97[[rule.threat.technique.subtechnique]]
98id = "T1078.004"
99name = "Cloud Accounts"
100reference = "https://attack.mitre.org/techniques/T1078/004/"
101
102[[rule.threat.technique]]
103id = "T1195"
104name = "Supply Chain Compromise"
105reference = "https://attack.mitre.org/techniques/T1195/"
106
107[[rule.threat.technique.subtechnique]]
108id = "T1195.002"
109name = "Compromise Software Supply Chain"
110reference = "https://attack.mitre.org/techniques/T1195/002/"
111
112[rule.threat.tactic]
113id = "TA0001"
114name = "Initial Access"
115reference = "https://attack.mitre.org/tactics/TA0001/"
116
117[[rule.threat]]
118framework = "MITRE ATT&CK"
119
120[[rule.threat.technique]]
121id = "T1059"
122name = "Command and Scripting Interpreter"
123reference = "https://attack.mitre.org/techniques/T1059/"
124
125[rule.threat.tactic]
126id = "TA0002"
127name = "Execution"
128reference = "https://attack.mitre.org/tactics/TA0002/"
129
130[[rule.threat]]
131framework = "MITRE ATT&CK"
132
133[[rule.threat.technique]]
134id = "T1213"
135name = "Data from Information Repositories"
136reference = "https://attack.mitre.org/techniques/T1213/"
137
138[[rule.threat.technique.subtechnique]]
139id = "T1213.003"
140name = "Code Repositories"
141reference = "https://attack.mitre.org/techniques/T1213/003/"
142
143[rule.threat.tactic]
144id = "TA0009"
145name = "Collection"
146reference = "https://attack.mitre.org/tactics/TA0009/"
References
Related rules
- GitHub Actions Workflow Modification Blocked
- New GitHub Self Hosted Action Runner
- GitHub Actions Unusual Bot Push to Repository
- GitHub Protected Branch Settings Changed
- GitHub App Deleted