Gitlab EE DoS by sending unauthenticated requests via POST /api/graphql (another bypass of Json validation middleware)
HackerOne report #3367019 by a92847865 on 2025-10-02, assigned to @gandrews7:
Report | Attachments | How To Reproduce
Report
NOTE! Thanks for submitting a report! Please note that initial triage is handled by HackerOne staff. They are identified with a
HackerOne triagebadge and will escalate to the GitLab team any. Please replace all the (parenthesized) sections below with the pertinent details. Remember, the more detail you provide, the easier it is for us to triage and respond quickly, so be sure to take your time filling out the report!
Summary
In latest release (v18.4.1), Json validation middleware was introduced by this security merge request. Now there're instance limits for depth, size and complexity (number of total elements) of HTTP request body (with mime-type: :json). I have submitted a bypass in which an attacker sets content-type header to application/xml (or other types except json).
The issue here is different. Malicious Json payload is totally within the limits enforcing by Json validation middleware. But a param (of type String) is parsed later by a Json parser causing uncontrolled resource consumption.
A normal json payload sending to /api/graphql looks like:
{"operationName":"abc", "query":"def","variables":{"var1":<value1>, "var2":<value2>}}
But "variables" could also be a String:
{"operationName":"abc", "query":"def","variables":"{\"var1\":<value1>, \"var2\":<value2>}"}
When parsing requests to get params (operationName, query and variables), an instance of GraphqlController will call Gitlab::Graphql::Variables.new(variable_info).to_h to build variables from variable_info:
https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/controllers/graphql_controller.rb#L296
def build_variables(variable_info)
Gitlab::Graphql::Variables.new(variable_info).to_h
end
If variable_info is a String, it will be parsed by JSON parser in this line.
So an attacker can embed a complex Json string inside "variables" value, passing Json validation check. Handling some malicious requests concurrently will consume all RAM in Gitlab EE instance.
Please be noted that /api/graphql endpoint is unauthenticated.
Steps to reproduce
(Step-by-step guide to reproduce the issue, including:)
- Get 2 GCP VM: VM1 (8 vCPU, 16 GB RAM) and VM2 (minimal specs)
- Install Gitlab EE on VM1 (https://docs.gitlab.com/install/package/ubuntu/)
- Generate payload (baddd.json) using
- Upload
to VM2 and run: gunzip baddd.json
- From VM2, send 3-4 requests to Gitlab EE instance on VM1 concurrently (see more in PoC video):
curl -v 'http://<Gitlab_host_name>/api/graphql' -v -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36' -H 'accept: */*' -H 'content-type: application/json' -H 'x-gitlab-version: 18.4.1-ee' --data-binary [@]baddd.json --insecure
- After 1 minute all RAM in VM1 is consumed and Gitlab EE instance becomes completely unreachable.
PoC video
<redacted>
Impact
Gitlab EE becomes completely unavailable to legitimate users
Examples
(If the bug is project related, please create an example project and export it using the project export feature)
(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)
(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.)
What is the current bug behavior?
Lack of size check before parsing Json payload (actually a JSON string) in below method:
https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/graphql/variables.rb#L23
def ensure_hash(ambiguous_param)
case ambiguous_param
when String
if ambiguous_param.present?
ensure_hash(Gitlab::Json.parse(ambiguous_param))
else
{}
end
when Hash
ambiguous_param
... // error handling codes
end
What is the expected correct behavior?
Should check size before parsing or use a more memory efficient parser.
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.)
Output of checks
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
Results of GitLab environment info
System information
System: Ubuntu 24.04
Proxy: no
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.10
Sidekiq Version:7.3.9
Go Version: unknown
GitLab information
Version: 18.4.1-ee
Revision: 270836848e7
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 16.10
URL: http://<redacted>
HTTP Clone URL: http://<redacted>/some-group/some-project.git
SSH Clone URL: git@<redacted>:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 14.45.2
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.4.1
- default Git Version: 2.50.1
(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)
Impact
Gitlab EE becomes completely unavailable to legitimate users
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
- baddd.json.gz
- badddjson.py
- <redacted>
How To Reproduce
Please add reproducibility information to this section:
3.