GitHub API Sensitive Resource Enumeration
editGitHub API Sensitive Resource Enumeration
editDetects a burst of GitHub api.request events from a single non-bot actor and source IP against sensitive routes such as Actions/Dependabot/environment secrets, public keys, git trees and contents, zipball archives, organization repository and member lists, webhooks, installations, and personal access tokens. Grouping by actor and source IP and requiring a high request count spread across several distinct sensitive routes separates broad enumeration from a single high-volume endpoint. This pattern is commonly used for credential discovery and source collection after token theft. Requires GitHub Enterprise audit log streaming with API Request Events enabled.
Rule type: esql
Rule indices: None
Severity: medium
Risk score: 47
Runs every: 10m
Searches indices from: now-11m (Date Math format, see also Additional look-back time)
Maximum alerts per execution: 100
References:
- https://securitylabs.datadoghq.com/articles/mapping-out-your-unknown-threat-hunters-guide-to-github/
- https://docs.github.com/en/enterprise-cloud@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/streaming-the-audit-log-for-your-enterprise#enabling-api-request-events
Tags:
- Domain: Cloud
- Domain: SaaS
- Platform: GitHub
- Use Case: Threat Detection
- Tactic: Discovery
- Tactic: Credential Access
- Tactic: Collection
- Data Source: Github
- Data Source: GitHub Audit Logs
- Rule Type: ES|QL
- Resources: Investigation Guide
Version: 1
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
editTriage 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 API Sensitive Resource Enumeration
api.request is a streaming-only audit action (available with GitHub Enterprise audit log streaming and API Request Events enabled). Sensitive routes include Actions/Dependabot/environment secrets, public keys, git trees, contents, zipball archives, organization repository lists, members, webhooks, installations, and personal access tokens. The rule groups requests by user.name and github.actor_ip in a 10-minute window and fires on >= 15 requests spanning >= 3 distinct sensitive routes, which distinguishes broad enumeration from a single high-volume route. Automated actors (github.actor_is_bot: true) and requests without an actor IP are excluded. An adversary who has stolen a token commonly enumerates these routes to locate secrets and source code before pulling them, so this activity frequently precedes successful credential access or source collection.
Possible investigation steps
-
Review the distinct routes probed (
Esql.github_route_values,Esql.github_route_unique_count) and the breadth across repositories and organizations (Esql.github_repo_unique_count,Esql.github_org_values) to gauge how targeted or broad the enumeration is. -
Identify the token and access method from
Esql.github_token_id_valuesandEsql.github_programmatic_access_type_values, and confirm whether the token belongs to known automation. -
Review
Esql.github_user_agent_valuesfor the client; treat user agents containing tooling signatures such astrufflehogornord-streamas credential-stealer tooling. -
Pivot on
github.actor_ipanduser.nameand correlate withgit.clone,repo.download_zip, and secret-scanning alerts from the same actor or token, which indicate the enumeration led to collection. - Check for a later run of successful (2xx) requests on secrets, trees, or contents from the same actor or token, which indicates successful enumeration rather than failed probing.
False positive analysis
- Internal inventory, backup, or secret-rotation automation and GitHub App servers that enumerate installations, repositories, and secrets. These typically use a known token and user agent; confirm and scope exceptions by actor, token, or user agent when expected.
- Security tooling that legitimately scans repositories for exposed secrets from a known source.
Response and remediation
- Revoke the token if the actor is not a known automation identity, and rotate any secrets exposed on the enumerated routes.
- Review PAT and OAuth grants for the actor, and tighten organization PAT access and approval policies.
- Block or monitor the source IP if the activity is clearly reconnaissance, and review whether any probed route later returned a success for the same actor.
- Escalate to incident response if the enumeration is followed by secret access, repository cloning, or ZIP downloads from the same actor or token.
Setup
editSetup
Requires the Elastic GitHub integration (logs-github.audit-*), GitHub Enterprise audit log streaming, and Enable API Request Events on the enterprise. api.request is not available via the REST audit-log API.
Rule query
editfrom logs-github.audit-* metadata _id, _index, _version
| where
data_stream.dataset == "github.audit" and event.action == "api.request" and
github.actor_ip is not null and (github.actor_is_bot is not null and github.actor_is_bot == false) and
(
github.route like "*secrets*" or
github.route like "*public-key*" or
github.route like "*git/trees*" or
github.route like "*contents*" or
github.route like "*zipball*" or
github.route like "*hooks*" or
github.route like "*installations*" or
github.route like "*members*" or
github.route like "*outside_collaborators*" or
github.route like "*personal-access-token*" or
github.route == "/organizations/:organization_id/repos"
)
| stats
Esql.event_count = COUNT(*),
Esql.github_route_unique_count = COUNT_DISTINCT(github.route),
Esql.github_repo_unique_count = COUNT_DISTINCT(github.repo),
Esql.github_route_values = VALUES(github.route),
Esql.github_org_values = VALUES(github.org),
Esql.github_programmatic_access_type_values = VALUES(github.programmatic_access_type),
Esql.github_user_agent_values = VALUES(github.user_agent),
Esql.agent_id_values = VALUES(agent.id),
Esql.data_stream_dataset_values = VALUES(data_stream.dataset),
Esql.data_stream_namespace_values = VALUES(data_stream.namespace),
Esql.github_token_id_values = VALUES(github.token_id)
by user.name, github.actor_ip, Esql.time_window = DATE_TRUNC(10 minutes, @timestamp)
| keep user.name, github.actor_ip, Esql.*
| where Esql.event_count >= 15 and Esql.github_route_unique_count >= 3
Framework: MITRE ATT&CKTM
-
Tactic:
- Name: Discovery
- ID: TA0007
- Reference URL: https://attack.mitre.org/tactics/TA0007/
-
Technique:
- Name: Cloud Service Discovery
- ID: T1526
- Reference URL: https://attack.mitre.org/techniques/T1526/
-
Tactic:
- Name: Credential Access
- ID: TA0006
- Reference URL: https://attack.mitre.org/tactics/TA0006/
-
Technique:
- Name: Unsecured Credentials
- ID: T1552
- Reference URL: https://attack.mitre.org/techniques/T1552/
-
Tactic:
- Name: Collection
- ID: TA0009
- Reference URL: https://attack.mitre.org/tactics/TA0009/
-
Technique:
- Name: Data from Information Repositories
- ID: T1213
- Reference URL: https://attack.mitre.org/techniques/T1213/
-
Sub-technique:
- Name: Code Repositories
- ID: T1213.003
- Reference URL: https://attack.mitre.org/techniques/T1213/003/