A developer can change status of a pipeline of a protected branch

HackerOne report #688205 by rpadovani on 2019-09-04, assigned to akelly:

Summary

A developer of a project can change the pipeline status of a protected branch to any value.
This allows to hide the status of protected pipelines, but also to skip the check "Pipeline must succeed before merging".

I think there are two issues, but are on a single endpoint so I was not sure if report them separately made any sense

Steps to reproduce

There are two issues here:

  • the behaviour of the POST /projects/:id/statuses/:sha API
  • the lack of authentication on the endpoint

To reproduce:

  • Create a failing gitlab-ci.yml (for the sake of simplicity, but you can create also a valid one and go the other way around).
    For example:
test:  
  script:  
    - COMMAND_NOT_FOUND  
  only:  
    - master  
  • Check the pipelines as developer of the project:
curl --header "PRIVATE-TOKEN: secret" "https://gitlab.com/api/v4/projects/12915125/pipelines" 

[{"id":80342609,"sha":"8c6f0f9e6fb0b71a51503655a512884bd53eaa69","ref":"master","status":"failed","web_url":"https://gitlab.com/rpadovani.test/public-project-secret-code/pipelines/80342609"}]%  

as a developer, I don't have edit rights on the master branch.
Still, I can invoke the API for setting the status of the build of the associated commit:

curl --request POST --header "PRIVATE-TOKEN: secret" "https://gitlab.com/api/v4/projects/12915125/statuses/8c6f0f9e6fb0b71a51503655a512884bd53eaa69?state=success&name=test"  

The important thing here is putting the name of the existing stage(s) in the parameter name.
When we query the pipelines endpoint again, we see the result is changed:

curl --header "PRIVATE-TOKEN: secret" "https://gitlab.com/api/v4/projects/12915125/pipelines"                   
[{"id":80342609,"sha":"8c6f0f9e6fb0b71a51503655a512884bd53eaa69","ref":"master","status":"success","web_url":"https://gitlab.com/rpadovani.test/public-project-secret-code/pipelines/80342609"}  

If you open the pipeline page, you see that something strange happened, but it is very confusing.
On the other hand, in the homepage of the project and in other places where the pipeline icon appears, there is nothing that suggest the pipeline originally failed and has been overwritten.

I see three issues here:

  • the behaviour of the API: it shouldn't accept names of other stages, or at least it shouldn't overwrite them
  • the authorization part: I know it is tricky, due how a commit could belong to multiple branches, but if exists a pair (commit, ref) where ref is protected, a developer shouldn't be able to change the status of the build
  • the UI is messy about this: there is no clear difference between "internal" and "external" status, and it is easy getting confused (not really a security issue, but confusion helps always the attacker)

Impact

Two main impacts:

  • On a protected ref: A developer can simulate a deploy was successful / failed while in the reality failed/succeeded.
  • In a MR the developer can avoid the "pipeline must succeeds to merge" check, manually setting it to success

Examples

I can give access to an example project

What is the current bug behavior?

The API allows overriding general status of a pipeline
The API doesn't do any check on the ref related to the commit - especially if the developer can access protected branches

What is the expected correct behavior?

No override, proper authorization

Output of checks

This bug happens on GitLab.com

Impact

  • On a protected ref: A developer can simulate a deploy was successful / failed while in the reality failed/succeeded.
  • In a MR the developer can avoid the "pipeline must succeeds to merge" check, manually setting it to success
Edited Jan 28, 2020 by GitLab SecurityBot
Assignee Loading
Time tracking Loading