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.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.
  • 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.

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

to-top