Several Failed Protected Branch Force Pushes by User
Detects a high number of failed force push attempts to protected branches by a single user within a short time frame. Adversaries may attempt multiple force pushes to overwrite commit history on protected branches, potentially leading to data loss or disruption of development workflows.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/12/16"
3integration = ["github"]
4maturity = "production"
5updated_date = "2026/01/12"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects a high number of failed force push attempts to protected branches by a single user within a short
11time frame. Adversaries may attempt multiple force pushes to overwrite commit history on protected branches,
12potentially leading to data loss or disruption of development workflows.
13"""
14from = "now-9m"
15interval = "8m"
16language = "esql"
17license = "Elastic License v2"
18name = "Several Failed Protected Branch Force Pushes by User"
19note = """ ## Triage and analysis
20
21> **Disclaimer**:
22> 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.
23
24### Investigating Several Failed Protected Branch Force Pushes by User
25
26This rule flags a single user generating multiple failed force push attempts to protected branches within a short span, indicating attempts to rewrite commit history and bypass branch protections. An attacker with a compromised maintainer role repeatedly tries to roll back a security fix, delete prior commits, and erase history entries before pushing a malicious revision. This activity threatens code integrity, disrupts pipelines, and can propagate harmful changes across repositories.
27
28### Possible investigation steps
29
30- Pull audit entries for the rejected updates to confirm the rejection reasons and the exact org/repo/branch targets, then reconstruct the timeline and sequence of attempts.
31- Verify the user's current and recent permissions, team membership, and role changes, and confirm whether any admin or ownership transfers occurred before the attempts.
32- Correlate the attempts with authentication and token activity (SSO logins, PAT/SSH key usage, IP/device fingerprints, geo), flagging any new or unusual sources.
33- Review branch protection settings and recent edits (require status checks, linear history, admin enforcement, force push exemptions) to detect policy tampering or misconfiguration.
34- Identify the specific commits the force pushes sought to overwrite by diffing the attempted ref against the protected branch head, prioritizing impacts to security fixes, release branches, or signed commits.
35
36### False positive analysis
37
38- During a repository migration or history cleanup, a maintainer runs a local script that loops through branches and tries to push rewritten commits with --force, but newly tightened branch protection rejects each attempt, resulting in multiple failures.
39- A developer who previously had a force-push exemption on a protected release branch loses that permission during a role or team change and continues their usual rebase-and-force-push workflow, causing several rapid rejected ref updates.
40
41### Response and remediation
42
43- Immediately block the user in the GitHub organization, revoke all active personal access tokens and SSH keys from their account, and force sign-out to stop further push attempts.
44- On each affected repository and branch (e.g., main, release/*), remove any force-push exemptions, enable “Include administrators,” require signed commits and status checks, and restrict push access to specific teams.
45- Purge staging artifacts by deleting any branches or tags the user created around the attempts, rotate the user’s password and regenerate PATs/SSH keys, and remove newly registered keys or OAuth apps added during the window.
46- Validate recovery by confirming the protected branch HEAD matches the last known good signed commit SHA, re-running CI for impacted repos, and creating a restore point tag for rapid rollback.
47- Escalate to incident response if any attempts targeted main or release branches, originated from a newly created PAT/SSH key or an unrecognized IP/device, or the user holds repo admin/organization owner rights.
48- Harden long term by enforcing org-wide 2FA/SSO, removing all standing force-push exemptions, requiring CODEOWNERS approvals on protected branches, and enabling audit alerts for branch protection edits and new credential creation.
49"""
50references = [
51 "https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack",
52 "https://trigger.dev/blog/shai-hulud-postmortem",
53 "https://posthog.com/blog/nov-24-shai-hulud-attack-post-mortem",
54]
55risk_score = 47
56rule_id = "8bd1c36a-2c4f-4801-a43d-ba696c13ffc2"
57severity = "medium"
58tags = [
59 "Domain: Cloud",
60 "Use Case: Threat Detection",
61 "Tactic: Impact",
62 "Tactic: Exfiltration",
63 "Data Source: Github",
64 "Resources: Investigation Guide",
65]
66timestamp_override = "event.ingested"
67type = "esql"
68query = '''
69from logs-github.audit-* metadata _id, _index, _version
70| where
71 data_stream.dataset == "github.audit" and
72 github.category == "protected_branch" and
73 event.action == "protected_branch.rejected_ref_update"
74| stats
75 Esql.document_count = COUNT(*),
76 Esql.github_org_values = values(github.org),
77 Esql.github_repo_values = values(github.repo),
78 Esql.github_branch_values = values(github.branch),
79 Esql.github_reasons_code_values = values(github.reasons.code),
80 Esql.github_reasons_message_value = values(github.reasons.message),
81 Esql.user_name_values = values(user.name),
82 Esql.agent_id_values = values(agent.id),
83 Esql.event_dataset_values = values(event.dataset),
84 Esql.data_stream_namespace_values = values(data_stream.namespace)
85
86 by user.name
87
88| keep Esql.*
89
90| where
91 Esql.document_count >= 5
92'''
93
94[[rule.threat]]
95framework = "MITRE ATT&CK"
96
97[[rule.threat.technique]]
98id = "T1485"
99name = "Data Destruction"
100reference = "https://attack.mitre.org/techniques/T1485/"
101
102[rule.threat.tactic]
103id = "TA0040"
104name = "Impact"
105reference = "https://attack.mitre.org/tactics/TA0040/"
106
107[[rule.threat]]
108framework = "MITRE ATT&CK"
109
110[[rule.threat.technique]]
111id = "T1020"
112name = "Automated Exfiltration"
113reference = "https://attack.mitre.org/techniques/T1020/"
114
115[[rule.threat.technique]]
116id = "T1567"
117name = "Exfiltration Over Web Service"
118reference = "https://attack.mitre.org/techniques/T1567/"
119
120[[rule.threat.technique.subtechnique]]
121id = "T1567.001"
122name = "Exfiltration to Code Repository"
123reference = "https://attack.mitre.org/techniques/T1567/001/"
124
125[rule.threat.tactic]
126id = "TA0010"
127name = "Exfiltration"
128reference = "https://attack.mitre.org/tactics/TA0010/"
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 Several Failed Protected Branch Force Pushes by User
This rule flags a single user generating multiple failed force push attempts to protected branches within a short span, indicating attempts to rewrite commit history and bypass branch protections. An attacker with a compromised maintainer role repeatedly tries to roll back a security fix, delete prior commits, and erase history entries before pushing a malicious revision. This activity threatens code integrity, disrupts pipelines, and can propagate harmful changes across repositories.
Possible investigation steps
- Pull audit entries for the rejected updates to confirm the rejection reasons and the exact org/repo/branch targets, then reconstruct the timeline and sequence of attempts.
- Verify the user's current and recent permissions, team membership, and role changes, and confirm whether any admin or ownership transfers occurred before the attempts.
- Correlate the attempts with authentication and token activity (SSO logins, PAT/SSH key usage, IP/device fingerprints, geo), flagging any new or unusual sources.
- Review branch protection settings and recent edits (require status checks, linear history, admin enforcement, force push exemptions) to detect policy tampering or misconfiguration.
- Identify the specific commits the force pushes sought to overwrite by diffing the attempted ref against the protected branch head, prioritizing impacts to security fixes, release branches, or signed commits.
False positive analysis
- During a repository migration or history cleanup, a maintainer runs a local script that loops through branches and tries to push rewritten commits with --force, but newly tightened branch protection rejects each attempt, resulting in multiple failures.
- A developer who previously had a force-push exemption on a protected release branch loses that permission during a role or team change and continues their usual rebase-and-force-push workflow, causing several rapid rejected ref updates.
Response and remediation
- Immediately block the user in the GitHub organization, revoke all active personal access tokens and SSH keys from their account, and force sign-out to stop further push attempts.
- On each affected repository and branch (e.g., main, release/*), remove any force-push exemptions, enable “Include administrators,” require signed commits and status checks, and restrict push access to specific teams.
- Purge staging artifacts by deleting any branches or tags the user created around the attempts, rotate the user’s password and regenerate PATs/SSH keys, and remove newly registered keys or OAuth apps added during the window.
- Validate recovery by confirming the protected branch HEAD matches the last known good signed commit SHA, re-running CI for impacted repos, and creating a restore point tag for rapid rollback.
- Escalate to incident response if any attempts targeted main or release branches, originated from a newly created PAT/SSH key or an unrecognized IP/device, or the user holds repo admin/organization owner rights.
- Harden long term by enforcing org-wide 2FA/SSO, removing all standing force-push exemptions, requiring CODEOWNERS approvals on protected branches, and enabling audit alerts for branch protection edits and new credential creation.
References
Related rules
- GitHub Private Repository Turned Public
- High Number of Closed Pull Requests by User
- High Number of Protected Branch Force Pushes by User
- GitHub Exfiltration via High Number of Repository Clones by User
- Github Activity on a Private Repository from an Unusual IP