Denial of Service due to Unbounded Object Creation via the parameter Scopes on the user's Personal Access Token
:warning: **Please read [the process](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/engineer.md) on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.** **[HackerOne report #2871791](https://hackerone.com/reports/2871791)** by `sim4n6` on 2024-11-30, assigned to `GitLab Team`: [Report](#report) | [Attachments](#attachments) | [How To Reproduce](#how-to-reproduce) ## Report ##### Summary The line [/master/app/controllers/user_settings/personal_access_tokens_controller.rb#L15](https://gitlab.com/gitlab-org/gitlab/-/blob/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. ```ruby 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](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/controllers/user_settings/personal_access_tokens_controller.rb#L13-L35) 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 `Cookie`s. 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](https://h1.sec.gitlab.net/a/e0c6c94b-2501-4da7-af44-47a0d55d70b8/poc.py). To run the updated version of the poc: ```bash 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](https://h1.sec.gitlab.net/a/9d30d4e4-a53c-4047-876b-05836b6cbef2/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`) ```yaml 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! * [poc.py](https://h1.sec.gitlab.net/a/e0c6c94b-2501-4da7-af44-47a0d55d70b8/poc.py) * [Enregistrement_2024-11-30_205719.mp4](https://h1.sec.gitlab.net/a/9d30d4e4-a53c-4047-876b-05836b6cbef2/Enregistrement_2024-11-30_205719.mp4) ## How To Reproduce Please add [reproducibility information] to this section: 1. 1. 1. [reproducibility information]: https://about.gitlab.com/handbook/engineering/security/#reproducibility-on-security-issues ## 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
issue