GitHub org takeover: owners, 2FA, SSO and IP allow list
GitHub organization takeover indicators in the audit log: new owners, 2FA requirement off, SAML SSO changed, IP allow list off, audit streaming removed.
TL;DR. A takeover is visible in the audit log as a short sequence of control changes: a new owner (org.update_member / org.add_member with permission: admin, business.add_admin), then defenses lowered (org.disable_two_factor_requirement, org.disable_saml or org.update_saml_provider_settings, ip_allow_list.disable, org.disable_oauth_app_restrictions, personal_access_token.* policy relaxed), and sometimes the stream to your SIEM removed (audit_log_streaming.destroy). Any one of them from an unexpected actor or IP is a high-priority finding. Two on the same actor is an incident.
Organization owners can change every repository, secret, member and security setting. An attacker who becomes an owner, or who controls an owner's session or token, does not need to be quiet. They only need to be faster than your review of the audit log. This playbook lists the control changes to hunt for and the order in which to undo them.
The takeover indicators
| Indicator | Audit actions | Analyzer rule | Severity |
|---|---|---|---|
| Owner added | org.update_member or org.add_member with permission: admin, business.add_admin | owner-granted | high |
| Admin on a repository for a collaborator | org.add_outside_collaborator, repo.add_member, repo.update_member with admin permission | collaborator-admin | medium |
| 2FA requirement off | org.disable_two_factor_requirement, business.disable_two_factor_requirement | two-factor-disabled | high |
| SAML SSO off or repointed | org.disable_saml, org.update_saml_provider_settings (and business. equivalents) | sso-changed | high |
| IP allow list off | ip_allow_list.disable, ip_allow_list.disable_for_installed_apps, ip_allow_list.disable_user_level_enforcement | ip-allowlist-disabled | high |
| Third-party app restrictions off | org.disable_oauth_app_restrictions | oauth-restrictions-disabled | medium |
| Token policy weakened | personal_access_token.access_restriction_disabled, .expiration_limit_unset, .auto_approve_grant_requests_enabled | pat-policy-weakened | medium |
| Audit stream removed | audit_log_streaming.destroy, audit_log_streaming.update | audit-stream-changed | high |
The ATT&CK mappings the rules use are T1098.003 Additional Cloud Roles for owner grants, T1556 Modify Authentication Process for 2FA and SSO, T1562.007 Disable or Modify Cloud Firewall for the IP allow list, and T1562.008 Disable or Modify Cloud Logs for streaming.
Under the verdict policy, two distinct high-severity rules on the same actor, token or IP make the verdict Compromised, even without a critical finding. That fits takeovers: owner-granted followed by two-factor-disabled from the same account is rarely an admin tidying up.
Reading each change
New owners
org.update_member records role changes. The permission field (and the user field for the affected account) tells you who was promoted. Check three things. Was the actor an existing owner using their usual IP and user agent? Is the promoted account new to the organization (look for org.add_member shortly before)? Did the new owner act soon after? An account promoted and then immediately creating tokens, keys or runners is the classic pattern.
Two-factor authentication
When an organization requires 2FA, members and outside collaborators without it are removed. Disabling the requirement lets weak accounts join or stay, and it is often the step before inviting an attacker-controlled account. Re-enabling it later removes any account that still has no 2FA, which is useful containment but also disruptive, so plan it.
SAML single sign-on
With SAML SSO (GitHub Enterprise Cloud), your identity provider decides who can reach organization resources, and tokens must be authorized for SSO. org.update_saml_provider_settings changes the issuer, the SSO URL or the certificate. An attacker who points them at an identity provider they control can then sign in as anyone they like. Compare the new values with your IdP's metadata. If your IdP is involved, its own logs are the next stop: the sibling tools cover Okta, Microsoft Entra ID and Google Workspace.
IP allow list
An IP allow list (Enterprise Cloud) blocks web, API and Git access from outside your ranges, including access with personal access tokens and SSH keys. That is why stolen credentials fail when it is on. Watch ip_allow_list.disable and also ip_allow_list_entry.create: adding the attacker's VPS to the list is quieter than disabling it, and the analyzer lists it in the timeline but does not raise a finding for it.
OAuth apps and personal access token policy
OAuth app access restrictions stop OAuth apps that members authorize from reading organization data until an owner approves them. Disabling them opens the door to consent phishing. The personal access token policy can block classic tokens, cap token lifetime and require approval for fine-grained tokens. Relaxing it just before a token is created and used is a sequence worth reconstructing.
Audit log streaming
Removing or repointing the stream (audit_log_streaming.destroy / .update) blinds the SIEM, while the in-product audit log keeps recording. It is also one of the few changes that only make sense for someone who expects to be investigated. See audit log streaming.
Order of remediation
- Preserve the audit log export before you revert anything. Reverting creates new events and makes the timeline harder to read.
- Remove unexpected owners and admins, then revoke their sessions, tokens, SSH keys and SSO authorizations.
- Restore authentication controls: 2FA requirement, SAML settings (verify them against the IdP), IP allow list, OAuth app restrictions, token policy.
- Restore the audit stream and work out what was lost while it was off. Buffering depends on the destination and on how long it was paused.
- Hunt for persistence the new owner could have created: deploy keys, apps, webhooks, runners, outside collaborators, and teams with broad access. The leaked token playbook has the list.
- Check repository-level controls as well. Owners can remove branch protection and rulesets anywhere. See branch protection tampering.
Related
- GitHub organization compromised? Incident response guide
- Leaked GitHub token: what to do and how to investigate
- Try these rules on your export with the in-browser analyzer