UPDATE: On 30 March 2023, GitHub updated the permissions associated with organisation and enterprise scopes. The admin:enterprise
and admin:org
scopes now include SAML identity information. Introducing enterprise:members_read
and org:members_read
scopes would have provided finer grained controls. The wider scopes is a reasonable outcome after waiting two and half years. Read the full changelog entry for more information about the change.
Many vendors use additional security features as a key selling point for their top of the range enterprise plan. Often the enterprise tier includes access to additional logging, single sign on, security policy enforcement and so on. These features are designed to give greater control and insight into potential security issues in the account. GitHub Enterprise Cloud includes all of this and more, but GitHub needs to fix their permissions before a malicious actor destroys a large organisation.
GitHub Enterprise Cloud allows customers to configure Single Sign On (SSO). This means once every 24 hours members of the organisation need to authenticate against the organisation’s identity provider before being granted access to resources in their GitHub organisation/s. People who have left the company lose access to the corporate GitHub repositories once their internal login is disabled. This is an important component of a broader set of software supply chain security controls.
While “System for Cross-domain Identity Management”, or SCIM for short, is available for user onboarding and off boarding within enterprise accounts, not all teams are in a position to use this. Even when SCIM can be used with GitHub enterprise accounts, there are times teams need to verify that all users in the enterprise account are valid. Due to the lack of granularity in GitHub’s organisation and enterprise oAuth scopes, running regular automated audits is significantly riskier than they need to be.
In order to read user objects associated with enterprise membership data only the admin:enterprise
scope. To read organisation membership data only read:org
scope is required. The scopes documentation provides the following descriptions, “[r]ead org and team membership, read org projects” for the org level scope and “[r]ead enterprise profile data” for the enterprise scope. One could reasonably assume this would include the SSO username data associated with members.
This isn’t the case. The SSO user data associated with GitHub users requires the admin:enterprise
scope for enterprise SSO. If SSO is configured at the organisation level the admin:org
scope is required to read the SSO user data.
These admin scopes grant significant access to the account.
In the case of the admin:org
scope, it is possible to manipulate all membership of the organisation including adding users, removing others, promoting and demoting organisation owners. A malicious insider who gained access to a token with this scope could grant themselves ownership access to the organisation and then have full control over the organisation and move on from there. An external user could grant themselves access to individual repositories via the external collaborators role, which bypasses SSO. This grants them access to commit to any repository they grant themselves access to.
The access bestowed by the admin:enterprise
scope is more powerful. Most enterprise accounts use SSO at the enterprise level as it centralises control and prevents organisation admins from messing with the configuration. Unfortunately if the enterprise admin token is compromised it is possible for an attacker to remove an organisation from the enterprise account.
If a token has both scopes, it is possible for an attacker to chain these overly broad scopes together to invite themselves to an organisation, remove that organisation from an enterprise account, which removes SSO, accept the invitation to the organisation and move on from there. While this would be noisey a properly prepared attacker could irreversibly destroy a large GitHub organisation in a matter of minutes.
While it is easy to say that teams need to protect their secrets, in practice things are more difficult. Credentials need to be stored somewhere. For example if the audit script is run by a Lambda function, the credentials will be stored in SSM Param Store or Secrets Manager. Your AWS admins have access to all of your secrets. Using minimally scoped tokens reduces the impact of a malicious insider.
There is a precedent for GitHub introducing finer grained scopes. As recently as October GitHub introduced new enterprise scope for managing self-hosted Action runners.
I first flagged this issue with GitHub over a year ago and there is no resolution. There isn’t even a commitment to fixing this issue. I am open to other suggestions, but I propose GitHub add enterprise:members_read
and org:members_read
that grants full read access to the users, verified email addresses and associated SSO data within an enterprise or organisation respectively.
Until this is fixed I can’t roll out automated GitHub user auditing and sleep well at night. All it takes is one motivated individual who gains access to a token with both admin scopes and it is game over. I can’t take this risk with my clients’ data and businesses.
If you have an enterprise account, I recommend you check periodically to see if users have generated personal access tokens with these scopes. Github provides the SSO authorisations for organisations endpoint for this purpose, and it only needs the read:org
scope.