DoS in Markdown preview caused by a bug in RepositoryLinkFilter

⚠️ 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 #3517644 by maksyche on 2026-01-20, imported by @greg:

Report | Attachments | How To Reproduce

HackerOne Analyst Summary

Summary of the issue

The researcher found DoS when opening Markdown file with the large number of ../ prefixes.

Steps to reproduce

  1. As the attacker, use SSH port forwarding to connect victim's GDK on attacker's localhost port 3000

Note: By default, GDK is not accessible publicly. This step is used to connect target GDK from the attacker's machine, and it is not required in the real attack.

  1. As the attacker, sign in attacker's account on GDK, with latest commit 18.9.0-pre 39aebb0dbcd -> Create a public project -> In the project, upload dos.md:

3517644-Step2-attacker-upload-file.png

  1. As the attacker, run following command to start attack -> Replace ATTACKER_GROUP/ATTACKER_PROJECT with attacker's group and project name:
while true; do curl -s -o /dev/null -w "%{time_total}s\n" "http://localhost:3000/ATTACKER_GROUP/ATTACKER_PROJECT/-/blob/main/dos.md?format=json&viewer=rich" & sleep 1; done
  1. (Optional) As the victim, check htop on GDK host machine. CPU or memory is not fully consumed:

3517644-Step4-victim-htop.png

  1. As the victim, try to access GitLab instance. You can see victim is not able to access it:

3517644-Step10-victim-cannot-visit-site.png

Impact statement

Malicious user can prevent other user from accessing the target GitLab instance.

If you have any questions or concerns about this report, feel free to assign it to H1 Triage via the action picker with a comment indicating your request.

Original Report

Hi

Summary

Current implementation of build_relative_path in repository_link_filter.rb removes the ../ prefix and shifts the whole string, which is a CPU-heavy operation. An attacker can create a small publicly accessible.md file with a huge number of these prefixes. A very small number of unauthenticated requests to read this file cause CPU overload and the instance becomes completely unusable.

Steps to reproduce
  1. Create a 1k-users virtual machine (I used AWS c5.2xlarge).
  2. Install Gitlab CE (I used Ubuntu). During the installation, I used the http://gitlab.example.com EXTERNAL_URL without https and updated local /etc/hosts to link the IP address of the machine and this domain.
  3. Log into Gitlab and create a public root/dos project with just a README file.
  4. Upload the dos.md file.
  5. Observe that when you try to preview the file, the request either fails or takes 20+ seconds to load.
  6. Since the project is public, you can query this file unauthenticated. Run this command from your machine to request the file every second (that's ~5% of the expected traffic for this instance type. I managed to make the instance unusable even with 1-2% of the traffic):
while true; do curl -s -o /dev/null -w "%{time_total}s\n" "http://gitlab.example.com/root/dos/-/blob/main/dos.md?format=json&viewer=rich" & sleep 1; done  
  1. Observe that Gitlab becomes completely unusable within seconds.
Impact

A small number of unauthenticated requests can make a Gitlab instance completely unusable within seconds.

What is the current bug behavior?

The server eats all the CPU resources within seconds even with less than 5% of traffic (I tested and even 1-2% of traffic can be enough).

What is the expected correct behavior?

Prefix calculation must be optimized. Something like this:

pos = 0  
while path[pos, 3] == '../'  
  parts.pop  
  pos += 3  
end  
path = path[pos..] || ''  
Relevant logs and/or screenshots

Here's the video PoC:

Results of GitLab environment info
sudo gitlab-rake gitlab:env:info 

System information  
System:		Ubuntu 24.04  
Current User:	git  
Using RVM:	no  
Ruby Version:	3.2.8  
Gem Version:	3.7.1  
Bundler Version:2.7.1  
Rake Version:	13.0.6  
Redis Version:	7.2.11  
Sidekiq Version:7.3.9  
Go Version:	unknown

GitLab information  
Version:	18.8.1  
Revision:	d0311b03573  
Directory:	/opt/gitlab/embedded/service/gitlab-rails  
DB Adapter:	PostgreSQL  
DB Version:	16.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  
Using LDAP:	no  
Using Omniauth:	yes  
Omniauth Providers: 

GitLab Shell  
Version:	14.45.5  
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: 	18.8.1  
- default Git Version: 	2.52.GIT  

Impact

A small number of unauthenticated requests can make a Gitlab instance completely unusable within seconds.

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section: