Denial of Service due to Unbounded Object Creation via the parameter Scopes on the user's Personal Access Token
HackerOne report #2871791 by sim4n6 on 2024-11-30, assigned to GitLab Team:
Report | Attachments | How To Reproduce
Report
Summary
The line /master/app/controllers/user_settings/personal_access_tokens_controller.rb#L15 is vulnerable to a Denial of Service (DoS) attack due to the possibility to create an infinite number of symbols using map(&:to_sym) from a user-controlled data.
scopes = params[:scopes].split(',').map(&:squish).select(&:present?).map(&:to_sym) unless params[:scopes].nil?
The issue is that symbols in Ruby are not garbage collected. When a symbol is created, it remains in memory for the program's lifetime. Thus, when uncontrolled symbol creation is possible it would result in memory exhaustion.
So, the parameter scopes gets split with a comma, whitespace stripped, and anything left becomes a symbol. A payload like a1, a2,a3 ,a4,a5 would results into the following symbols [:a1, :a2, :a3, :a4, :a5] in memory remaining there indefinitely.
Steps to reproduce
I crafted a proof-of-concept in Python that triggers the index method with a POST request instead of a GET request.
To use it, you need to update the following values:
- A valid
X-CSRF-Token - An authenticated user
Cookies. - Gitlab instance hostname in the variable
url.
To do so, intercept the GET request to http://OWN_GITLAB_INSTANCE/-/user_settings/personal_access_tokens and plug the extracted three values into the poc .
To run the updated version of the poc:
for i in $(seq 1 20); do python3 poc.py 1_000_000 & done
Impact
The Gitlab instance can be taken down entirely and all its running services fail (DoS).
Examples
(If the bug is project-related, please create an example project and export it using the project export feature)
Nope
(If you are using an older version of GitLab, this will also help determine whether the bug has been fixed in a more recent version)
Nope
(If the bug can be reproduced on GitLab.com without violating the Rules of Engagement as outlined in the program policy, please provide the full path to the project.)
Did not try there due to the DoS rules of engagement.
What is the current bug behavior?
(What actually happens, include relevant screenshots, API results, or complete HTTP requests)
Resource exhaustion happens due to unbounded symbol creation via the parameter scopes.
What is the expected correct behavior?
(What you should see instead, include relevant screenshots, API results, or complete HTTP requests)
One way of fixing this issue is :
- Limit the parameter scopes input size.
- Use an allowlist of the valid scopes: read_api, create_runner, etc.
- Avoid
to_symfor untrusted data.
Relevant logs and/or screenshots
(Paste any relevant logs - please use code blocks (```) to format console output,
logs, and code as it's very hard to read otherwise.)
I made a recording for the whole Denial of Service attack:
Enregistrement_2024-11-30_205719.mp4
Output of checks
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
Did not check in gitlab.com
Results of GitLab environment info
(For installations with omnibus-gitlab package run and paste the output of:
sudo gitlab-rake gitlab:env:info)
(For installations from source run and paste the output of:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production)
System information
System: Ubuntu 24.10
Proxy: no
Current User: git
Using RVM: no
Ruby Version: 3.2.5
Gem Version: 3.5.23
Bundler Version:2.5.11
Rake Version: 13.0.6
Redis Version: 7.0.15
Sidekiq Version:7.2.4
Go Version: unknown
GitLab information
Version: 17.6.1-ee
Revision: 5f698b15f67
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 14.11
URL: http://gitlab.example.com
HTTP Clone URL: http://gitlab.example.com/some-group/some-project.git
SSH Clone URL: git@gitlab.example.com:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 14.39.0
Repository storages:
- default: unix:/var/opt/gitlab/gitaly/gitaly.socket
GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell
Gitaly
- default Address: unix:/var/opt/gitlab/gitaly/gitaly.socket
- default Version: 17.6.1
- default Git Version: 2.47.0
Impact
The GitLab instance can be completely shut down, causing all its running services to fail, resulting in a denial of service (DoS).
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section:
Proposal
Though the original assumption that symbols are not garbage collected is wrong, there is a risk of DoS when WAF and rate limiting are properly configured to prevent large payloads. We can reduce this by reducing the object creation on the PAT creation endpoint