GitHub Exfiltration via High Number of Repository ZIP Downloads
Detects a high number of GitHub repository ZIP archive downloads by a single non-bot user from one source IP within a
short window. The repo.download_zip action retrieves a full repository archive over HTTP, so adversaries use it to
collect source code without a git clone (which clone-threshold rules watch), including with classic PATs,
fine-grained PATs, or OAuth tokens. Grouping by actor and source IP and requiring a high archive-download count
separates bulk source collection from occasional developer downloads.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/09/22"
3integration = ["github"]
4maturity = "production"
5updated_date = "2026/11/03"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects a high number of GitHub repository ZIP archive downloads by a single non-bot user from one source IP within a
11short window. The `repo.download_zip` action retrieves a full repository archive over HTTP, so adversaries use it to
12collect source code without a `git clone` (which clone-threshold rules watch), including with classic PATs,
13fine-grained PATs, or OAuth tokens. Grouping by actor and source IP and requiring a high archive-download count
14separates bulk source collection from occasional developer downloads.
15"""
16false_positives = [
17 """
18 Developers downloading archives for offline review, backup jobs, or CI that fetches zipballs. Confirm the actor,
19 token type, and whether the repositories are expected.
20 """,
21]
22from = "now-9m"
23interval = "8m"
24language = "esql"
25license = "Elastic License v2"
26name = "GitHub Exfiltration via High Number of Repository ZIP Downloads"
27note = """## Triage and analysis
28
29> **Disclaimer**:
30> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
31
32### Investigating GitHub Exfiltration via High Number of Repository ZIP Downloads
33
34`repo.download_zip` is the audit action for repository archive/zipball downloads. Mass ZIP collection is a clone alternative that `git.clone` threshold rules do not catch, so it is a useful source-collection path for an adversary with a stolen token. The rule groups downloads by `user.name` and `github.actor_ip` in a 10-minute window, requires a high archive-download count, and excludes automated actors (`github.actor_is_bot: true`) and requests without an actor IP.
35
36### Possible investigation steps
37
38- Identify `user.name`, `github.actor_ip`, `github.programmatic_access_type`, and the token (`Esql.github_token_id_values`), and confirm whether the token belongs to a known developer or automation identity.
39- Review the distinct repositories downloaded (`Esql.github_repo_values`, `Esql.github_repo_unique_count`) and the organizations (`Esql.github_org_values`), and whether those repositories are private or sensitive.
40- Review the client from `Esql.github_user_agent_values`; tooling or scripted user agents on a high download count are a stronger signal than a browser.
41- Correlate with `git.clone`, `api.request` enumeration, collaborator changes, and secret-scanning alerts from the same token or actor IP to establish whether this is part of a broader collection campaign.
42- Check the authentication context for the actor around the window for signs of a stolen token, such as a new `github.actor_ip` or unfamiliar user agent for that token.
43
44### False positive analysis
45
46- Approved bulk archive export, backup, or disaster-recovery tooling, and CI that fetches zipballs. Confirm the actor and token type and, if expected, scope exceptions by actor, token, or user agent.
47- A developer legitimately downloading several archives for offline review over a short period.
48
49### Response and remediation
50
51- Revoke the token if the actor is not a known automation identity, and review repository access and PAT/OAuth grants for the actor.
52- Review the downloaded repositories for sensitive content and rotate any secrets they contain.
53- Hunt for subsequent off-platform transfer of the archives from the same host or IP.
54- Escalate to incident response if private or sensitive repositories were downloaded, or if the downloads coincide with other collection or credential-access activity by the same actor.
55"""
56references = [
57 "https://securitylabs.datadoghq.com/articles/mapping-out-your-unknown-threat-hunters-guide-to-github/",
58 "https://github.blog/news-insights/company-news/security-alert-stolen-oauth-user-tokens/",
59 "https://docs.github.com/en/rest/repos/contents#download-a-repository-archive-zip",
60]
61risk_score = 47
62rule_id = "0fe75796-0258-4e19-a762-26228edcc468"
63severity = "medium"
64tags = [
65 "Domain: Cloud",
66 "Domain: SaaS",
67 "Platform: GitHub",
68 "Use Case: Threat Detection",
69 "Tactic: Collection",
70 "Tactic: Exfiltration",
71 "Data Source: Github",
72 "Data Source: GitHub Audit Logs",
73 "Rule Type: ES|QL",
74 "Resources: Investigation Guide",
75]
76timestamp_override = "event.ingested"
77type = "esql"
78
79query = '''
80from logs-github.audit-* metadata _id, _index, _version
81| where
82 data_stream.dataset == "github.audit" and event.action == "repo.download_zip" and
83 github.actor_ip is not null and github.actor_is_bot == false
84| stats
85 Esql.event_count = COUNT(*),
86 Esql.github_repo_unique_count = COUNT_DISTINCT(github.repo),
87 Esql.github_org_values = VALUES(github.org),
88 Esql.github_repo_values = VALUES(github.repo),
89 Esql.github_programmatic_access_type_values = VALUES(github.programmatic_access_type),
90 Esql.github_user_agent_values = VALUES(github.user_agent),
91 Esql.agent_id_values = VALUES(agent.id),
92 Esql.data_stream_dataset_values = VALUES(data_stream.dataset),
93 Esql.data_stream_namespace_values = VALUES(data_stream.namespace),
94 Esql.github_token_id_values = VALUES(github.token_id)
95 by user.name, github.actor_ip, Esql.time_window = DATE_TRUNC(10 minutes, @timestamp)
96| keep user.name, github.actor_ip, Esql.*
97| where Esql.event_count >= 8
98'''
99
100
101[[rule.threat]]
102framework = "MITRE ATT&CK"
103[[rule.threat.technique]]
104id = "T1020"
105name = "Automated Exfiltration"
106reference = "https://attack.mitre.org/techniques/T1020/"
107[[rule.threat.technique]]
108id = "T1567"
109name = "Exfiltration Over Web Service"
110reference = "https://attack.mitre.org/techniques/T1567/"
111[[rule.threat.technique.subtechnique]]
112id = "T1567.001"
113name = "Exfiltration to Code Repository"
114reference = "https://attack.mitre.org/techniques/T1567/001/"
115
116
117[rule.threat.tactic]
118id = "TA0010"
119name = "Exfiltration"
120reference = "https://attack.mitre.org/tactics/TA0010/"
121[[rule.threat]]
122framework = "MITRE ATT&CK"
123[[rule.threat.technique]]
124id = "T1213"
125name = "Data from Information Repositories"
126reference = "https://attack.mitre.org/techniques/T1213/"
127[[rule.threat.technique.subtechnique]]
128id = "T1213.003"
129name = "Code Repositories"
130reference = "https://attack.mitre.org/techniques/T1213/003/"
131
132
133
134[rule.threat.tactic]
135id = "TA0009"
136name = "Collection"
137reference = "https://attack.mitre.org/tactics/TA0009/"
138
139[rule.investigation_fields]
140field_names = [
141 "user.name",
142 "github.actor_ip",
143 "Esql.time_window",
144 "Esql.event_count",
145 "Esql.github_repo_unique_count",
146 "Esql.github_org_values",
147 "Esql.github_repo_values",
148 "Esql.github_programmatic_access_type_values",
149 "Esql.github_user_agent_values",
150 "Esql.github_token_id_values",
151]
Triage and analysis
Disclaimer: This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
Investigating GitHub Exfiltration via High Number of Repository ZIP Downloads
repo.download_zip is the audit action for repository archive/zipball downloads. Mass ZIP collection is a clone alternative that git.clone threshold rules do not catch, so it is a useful source-collection path for an adversary with a stolen token. The rule groups downloads by user.name and github.actor_ip in a 10-minute window, requires a high archive-download count, and excludes automated actors (github.actor_is_bot: true) and requests without an actor IP.
Possible investigation steps
- Identify
user.name,github.actor_ip,github.programmatic_access_type, and the token (Esql.github_token_id_values), and confirm whether the token belongs to a known developer or automation identity. - Review the distinct repositories downloaded (
Esql.github_repo_values,Esql.github_repo_unique_count) and the organizations (Esql.github_org_values), and whether those repositories are private or sensitive. - Review the client from
Esql.github_user_agent_values; tooling or scripted user agents on a high download count are a stronger signal than a browser. - Correlate with
git.clone,api.requestenumeration, collaborator changes, and secret-scanning alerts from the same token or actor IP to establish whether this is part of a broader collection campaign. - Check the authentication context for the actor around the window for signs of a stolen token, such as a new
github.actor_ipor unfamiliar user agent for that token.
False positive analysis
- Approved bulk archive export, backup, or disaster-recovery tooling, and CI that fetches zipballs. Confirm the actor and token type and, if expected, scope exceptions by actor, token, or user agent.
- A developer legitimately downloading several archives for offline review over a short period.
Response and remediation
- Revoke the token if the actor is not a known automation identity, and review repository access and PAT/OAuth grants for the actor.
- Review the downloaded repositories for sensitive content and rotate any secrets they contain.
- Hunt for subsequent off-platform transfer of the archives from the same host or IP.
- Escalate to incident response if private or sensitive repositories were downloaded, or if the downloads coincide with other collection or credential-access activity by the same actor.
References
Related rules
- GitHub Token Clone Push or ZIP from Unusual ASN
- GitHub Webhook Created or Configuration Changed
- GitHub API Sensitive Resource Enumeration
- High Number of Protected Branch Force Pushes by User
- GitHub Exfiltration via High Number of Repository Clones by User