Summary
| Forestall ACL Alias | GH_MAINTAINER |
| Edge Type | Attack Path |
| Affected Object Types | GitHub Teams |
| Exploitation Certainty | Certain |
| GitHub Role / Permission | Team maintainer role |
Description
GH_MAINTAINER represents that a user holds the maintainer role on a GitHub team. A team maintainer can manage the team without being an organization owner:
- Add and remove team members.
- Promote other members to maintainer.
- Edit team settings (name, description, visibility) and manage the team’s repository access requests.
- Create and manage nested child teams.
This is a traversable attack path because controlling a maintainer means controlling the team’s membership. An attacker who compromises a maintainer can add an attacker-controlled account to the team, and that account then inherits everything the team can reach — every repository granted to the team via GH_HAS_ACCESS, and the access of any nested child teams.
In effect, a maintainer indirectly holds all of the team’s access, plus the ability to hand that access to anyone. Plain membership (no management rights) is modelled separately by GH_IN_TEAM.
Identification
GitHub Web Interface
- Open the organization → Teams → select the team.
- Open the Members tab. Each member’s role is shown; filter or look for Maintainer.
GitHub CLI
List the maintainers of a team:
gh api "orgs/{org}/teams/{team_slug}/members?role=maintainer" --paginate \ --jq '.[].login'Check a specific user’s role in a team:
gh api "orgs/{org}/teams/{team_slug}/memberships/{username}" --jq '.role'Exploitation
A maintainer does not need any further privilege to weaponize the team — managing membership is the capability:
-
Add an attacker-controlled account to the team. The added account immediately inherits every repository the team can access.
gh api --method PUT "orgs/{org}/teams/{team_slug}/memberships/{attacker}" -f role=member -
Reach the team’s repositories. Any repository the team holds
write/maintain/adminon is now reachable by the attacker — enabling direct pushes, workflow tampering, or full repo control depending on the team’s permission. -
Establish persistence. Promote the attacker account to maintainer so the foothold survives, or quietly keep it as a member among many.
gh api --method PUT "orgs/{org}/teams/{team_slug}/memberships/{attacker}" -f role=maintainer -
Pivot through nested teams. If the team has child teams, the attacker can manage those as well, widening the blast radius.
Mitigation
-
Minimize team maintainers. Keep the maintainer role to the few people who genuinely administer the team; everyone else should be a plain member.
-
Review maintainers on privileged teams. Treat maintainers of teams that hold
admin/writeon sensitive repositories as elevated access and audit them regularly. -
Prefer organization-level management for sensitive changes. Where possible, route membership changes through an approved process rather than leaving them to individual maintainers.
-
Require 2FA for maintainers. A maintainer account without 2FA is a high-value target — enforce two-factor org-wide (see
FS6011).
Detection
| Event | Description |
|---|---|
team.add_member |
A user was added to the team |
team.remove_member |
A user was removed from the team |
team.promote_maintainer |
A member was promoted to maintainer |
team.demote_maintainer |
A maintainer was demoted to member |
Alert on members being added to privileged teams and on promotions to maintainer, especially when performed outside an approved change process. An unexpected actor adding an account to a team that holds sensitive repository access is a strong indicator of compromise.