A deactivated user can access data through GraphQL
HackerOne report #1192460 by joaxcar
on 2021-05-11, assigned to @dcouture:
Report
Summary
A deactivated user should not be able to access information through the API. This rule is not enforced when making requests through the GraphQL endpoint.
When reading through the changelog for 13.11.2 i noticed that the rule for a deactivated user allows for :log_in (as it should) but it is restricted from :access_api(as it should) link. The GraphQL endpoint does not seam to use this rules when authorizing a user. I guess GraphQL only checks for api scope on the user.
This opens for three potential problems:
- A user using its account through the GraphQL API (through some script or similar) would not get a warning that the account is deactivated. This could lead to the account being removed if the entities controlling the GitLab instance has any automatic procedures deleting accounts. When reading about the deactivation feature I got the impression that most admins requesting the feature would use it in automated "cleanings" of their user base. I could see how an admin could implement a "deactivate after 90 days inactivity" and "delete after 180 days inactivity" rule or similar. This could lead to an account being "in use" through GraphQL could get deleted without proper warnings.
- An admin could use deactivated accounts as "bots" or "service accounts" bypassing the billing of these accounts. (an admin can create users and deactivate them directly, before ever using the account)
- The fact that the account should not be able to do this. An admin reading the docs are under the assumption that a deactivated account is blocked from using the API. An inactive user could have left some form of scripts running that would keep on using resources on the GitLab instance, which I guess the admin would like to remediate by deactivating the account.
as of 13.10.4: A deactivated user can (without activating its account) use read queries on the GraphQL endpoint. The latest security patch removes the ability to use mutations due to the fact that
rule { deactivated }.policy do
prevent :access_git
prevent :access_api
prevent :receive_notifications
prevent :use_slash_commands
end
prevents :access_api, and
rule { ~can?(:access_api) }.prevent :execute_graphql_mutation
prevents from using mutations if I understand the code correctly.
tested on 13.11.1: (Prior to latest security patch 13.11.2) A deactivated user can (without activating its account) use queries and mutations on the GraphQL endpoint.
Steps to reproduce
Unlimited service accounts
- Login as admin
- Create a user
- Deactivate the user
- Create an api token for the deactivated user
- Use the token in GraphQL requests such as (replacing url and token)
curl 'https://gitlab.com/api/graphql' -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'Authorization: Bearer <<TOKEN>>' --data '{"query":"{\n currentUser{id}\n}"}'d
User with deactivated account
- Use any old token from your deactivated account in requests such as
curl 'https://gitlab.com/api/graphql' -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'Authorization: Bearer <<TOKEN>>' --data '{"query":"{\n currentUser{id}\n}"}'d
or on servers prior to 13.11.2 (tested on 13.11.1)
- Login as admin
- Create a user
- Deactivate the user
- Create an api token for the deactivated user
- Add the user to a project with (use admin token, and a real project id)
curl --header "Authorization: Bearer <<ADMIN TOKEN>>" "https://gitlab.domain.com/api/v4/projects//members" --data "user_id=2&access_level=40"
- Then perform a mutation with the disabled account:
curl 'https://gitlab.domain.com/api/graphql' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Bearer <<DEACTIVATEDTOKEN>>' --data '{"query":"mutation {\n labelCreate(input:{title:\"deactivated\", projectPath:\"test1/test1\"}){\n errors\n label{\n id\n }\n }\n}"}'
to create a label in the project.
Impact
For GitLab it could lead to loss of revenue due to the ability to create accounts that are not billabel but "usable". At the moment the GraphQL API is a bit limited but will probably grow in scope.
For users. Running the risk of missing warnings about disabled accounts. Could lead to deletion of account if admins does not notice that the account is being used.
Examples
I would guess that this is affecting GitLab.com but can not create a disabled account there.
What is the current bug behavior?
With a token from a disabled account the REST API gives:
curl --header "Authorization: Bearer jKSvxhuDN-Noag6N-w7R" "http://gitlab.joaxcar.com/api/v4/user"
{"message":"403 Forbidden - Your account has been deactivated by your administrator. Please log back in from a web browser to reactivate your account at http://gitlab.joaxcar.com"}
with GraphQL
curl 'http://gitlab.joaxcar.com/api/graphql' -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'Authorization: Bearer jKSvxhuDN-Noag6N-w7R' --data '{"query":"{\n currentUser{id}\n}"}'
{"data":{"currentUser":{"id":"gid://gitlab/User/15"}}}
What is the expected correct behavior?
GraphQL should give a warning as the REST API and block disabled users from accessing data.
Results of GitLab environment info
System information
System:
Current User: gitlab
Using RVM: no
Ruby Version: 3.0.1p64
Gem Version: /usr/lib/ruby/2.7.0/bundler/spec_set.rb:86:in `block in materialize': Could not find rake-13.0.3 in any of the sources (Bundler::GemNotFound)
from /usr/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `map!'
from /usr/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `materialize'
from /usr/lib/ruby/2.7.0/bundler/definition.rb:170:in `specs'
from /usr/lib/ruby/2.7.0/bundler/definition.rb:237:in `specs_for'
from /usr/lib/ruby/2.7.0/bundler/definition.rb:226:in `requested_specs'
from /usr/lib/ruby/2.7.0/bundler/runtime.rb:101:in `block in definition_method'
from /usr/lib/ruby/2.7.0/bundler/runtime.rb:20:in `setup'
from /usr/lib/ruby/2.7.0/bundler.rb:149:in `setup'
from /usr/lib/ruby/2.7.0/bundler/setup.rb:20:in `block in <top (required)>'
from /usr/lib/ruby/2.7.0/bundler/ui/shell.rb:136:in `with_level'
from /usr/lib/ruby/2.7.0/bundler/ui/shell.rb:88:in `silence'
from /usr/lib/ruby/2.7.0/bundler/setup.rb:20:in `<top (required)>'
from <internal:/usr/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
from <internal:/usr/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
Bundler Version:unknown
Rake Version: 13.0.3
Redis Version: 6.2.3
Git Version: 2.31.1
Sidekiq Version:5.2.9
Go Version: go1.16.4 linux/amd64
GitLab information
Version: 13.10.4
Revision: e11cc45d59e
Directory: /usr/share/webapps/gitlab
DB Adapter: PostgreSQL
DB Version: 13.2
URL: http://gitlab.joaxcar.com
HTTP Clone URL: http://gitlab.joaxcar.com/some-group/some-project.git
SSH Clone URL: gitlab@gitlab.joaxcar.com:some-group/some-project.git
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 13.17.0
Repository storage paths:
- default: /var/lib/gitlab/repositories
GitLab Shell path: /usr/share/webapps/gitlab-shell
Git: /usr/bin/git
Impact
A user with a disabled account can access the GraphQL API without activating the account. Running the risk of missing warnings about disabled accounts. Could lead to deletion of account if admins does not notice that the account is being used. Or accessing data without admins knowing the account is in use.
An admin could create accounts that are not billable but "usable". By creating disables accounts and use them through GraphQL.
I put it at medium due to the risk of data loss if not getting proper warnings and the fact that it has access to the API even if explicitly told that it should not in the documentation. But feel free to lower the severity if you disagree.
How To Reproduce
Please add reproducibility information to this section: