Denial of Service due to Unbounded Object Creation via the parameter Scopes on the user's Personal Access Token

⚠️ Please read the process on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.

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:

  1. A valid X-CSRF-Token
  2. An authenticated user Cookies.
  3. 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 poc.py.

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 :

  1. Limit the parameter scopes input size.
  2. Use an allowlist of the valid scopes: read_api, create_runner, etc.
  3. Avoid to_sym for 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

Edited by Aboobacker MK