The following (slightly modified) advisory was sent to GitLab using Hackerone on 19th June 2020.
During the Responsible Disclosure process it turned out, that the vulnerability was known for quite some time. After elaborating further on the impact, a security release fixed the issue after few weeks.
GitLab can be configured to act as OpenID Connect Identity Provider. If this is the case, in a Single Sign-On scenario a relying party allows GitLab users to sign in using their existing account.
As defined within the OpenID Connect specification, the user has to give explicit consent which information is shared with the relying party :
Once the End-User is authenticated, the Authorization Server MUST obtain an authorization decision before releasing information to the Relying Party. When permitted by the request parameters used, this MAY be done through an interactive dialogue with the End-User that makes it clear what is being consented to or by establishing consent via conditions for processing the request or other means (for example, via previous administrative consent). […]
GitLab implements the following dialog to ask End-Users for consent:
Additionally, GitLab allows users to revoke their consent for already granted accesses:
But if a user gave consent once, starts a new OIDC Authentication Flow and then revokes consent after a fresh code was issued to the Relying Party, the code is not correctly revoked and can be redeemed for an id_token and access_token/refresh_token at GitLab’s Token Endpoint . Even worse: The revoked authorized Application reappears at the “Authorized applications” section and restores the previously granted permissions persistently.
Steps to Reproduce
Prerequisite: The user has to give consent to share information with the Relying Party once - i.e. the users logs in at the Service Provider using his GitLab account:
0) Start an OpenID Connect Login flow from your Service Provider (in our example a Keycloak instance).
1) At first we enable Burp to intercept all request. The users starts an OIDC authentication flow at the Service Provider. We manually forward all requests up to the “Token Request” . We save this request for later use (by sending it to Burp’s “Repeater”) and drop it at the “Intercept” tab. Afterwards we disable the interception mode.
2) At GitLab the user revokes the Authorization for the Application
3) Afterwards the saved “Token Request” from (1) is send to GitLab’s Token Endpoint http://gitlab.local/oauth/token - we observe that we actually receive fresh tokens and an id_token from GitLab in response.
Token Request to GitLab’s Token Endpoint including the previously issued code:
A maliciously acting Service Provider could obtain sensitive data (id_token) and Bearer tokens (access_token/refresh_token) without user’s consent (as the consent was explicitly revoked beforehand). Even worse, the malicious actor may restore the previously given and then revoked consent persistently, resulting in future access to resources and information.
Note: I am not a lawyer. Nevertheless, my naïve legal perspective on this vulnerability is described in the following.
Consent is a central aspect of the GDPR, without explicit consent no personal data is allowed to be processed or shared. Users may revoke their consent at any time, entities processing the data must not proceed after this point in time.
If you apply this to GitLab’s behavior, GitLab initially asks for consent to share personal data (i.e. the id_token) with third parties (the Service Provider).
But: Due to this flaw, GitLab proceeds sharing personal data, if the user has explicitly revoked consent for this.
In order to mitigate the above described issue, all tokens and code values issued for a Client need to be invalidated if an End-User revokes the permissions for this Client.