Skip to content

Arbitrary PUT request as victim user through Sentry error list

HackerOne report #1600343 by joaxcar on 2022-06-14, assigned to @nmalcolm:

Report | Attachments | How To Reproduce

Report

Summary

By spoofing a Sentry server a malicious user can inject a path traversal in the code that generate function buttons on the "Error tracking list" page in a project. If a victim user click any of the action buttons (ignore or resolve) the UI will issue a PUT request with a valid CSRF token to any endpoint on the GitLab instance. By abusing this an attacker can in worst case elevate its privilege to administrator. Many other attacks are possible, and the functionality is to be considered "normal workflow" as you will see, even if some user interaction is needed.

Details

A maintainer of a project can activate error tracking docs in a project by configuring a Sentry server URL, a project name and an access token. After enabling error tracking any error connected to the project in Sentry will be listed under https://gitlab.com/groupname/projectname/-/error_tracking. This list contains a card for every error showing the title, a description and two action buttons to either ignore or resolve the error. The title of the error work as a link to the error details page.

error_list.png

Both the details page link and the action buttons get their related URLs by concatenating the error ID with a preexisting URL. This error ID is taken from the response from the server and used in the URLs without any escaping or sanitization. By spoofing the Sentry server one can thus inject anything in these URLs. By giving any error an ID beginning with ../../../../api/v4/ any click on the action buttons will now send PUT requests to the GitLab API.

This is the put request
https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/error_tracking/services/index.js

  updateErrorStatus(endpoint, status) {  
    return axios.put(endpoint, { status });  
  },  

and here is the endpoint generated. As you can see there is no sanitation to the errorId at this point
https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/error_tracking/components/error_tracking_list.vue

getIssueUpdatePath(errorId) {  
  return `/${this.projectPath}/-/error_tracking/${errorId}.json`;  
}

But the problem goes deeper where there is no validation of the error data input in the https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/error_tracking lib. This allows an error ID like ../../../../api/v4 to be accepted.

Now giving an error the ID ../../../../api/v4/users/4?admin=true# will generate actions that when clicked by an administrator will elevate the user with ID 4 to administrator. The trailing # is just to remove any trailing data from the requests.

To make it more likely for a victim to click the action buttons the attacker can give all errors in the list the title (a blank space), this will effectively remove the "error details page" link, leaving only the infected buttons and the error summary. (see image)

list with empty title, links does not exist only the action buttons

no_title.png

The attacker need maintainer access to add a spoofed Sentry server, but the attacker can create a new project, add the server, invite any user as a developer and then convince the user of "cleaning up the errors".

To attack an administrator the attacker can generate the infected project and then ask an administrator to "take a look at the error list in my project, it seems to not respond to actions, can you take a look at this. All errors in the list should be moved to ignore!".

Steps to reproduce

If possible test with an admin account, if you only want to see a working minimal PoC use a regular account and click the second "update user status" error injection. By using my spoof server the user that will be made admin is the user with ID 4. Make sure to revert this after the test!

  1. Create two users attacker (normal user) and victim (admin user)
  2. Log in to the GitLab instance as attacker
  3. Create a new project, call it project01
  4. Go to https://gitlab.example.com/attacker/project01/-/settings/operations and expand the tab "Error tracking"
  5. Configure the fields with my spoofed server
  1. Click Connect on the side of the Token field
  2. There should now exist a project in the project dropdown, select Test | test
  3. Make sure Active is checked and click save
  4. If the victim is not admin, go to members page and invite the user to the project as Developer, if victim is administrator just move on
  5. Log out and log back in as victim
  6. Go to https://gitlab.com/attacker/project01/-/error_tracking
  7. Click any of the action buttons on the right on the first error
  8. Click any of the action buttons on the right on the second error
  9. Refresh the page
  10. Click the user icon in the top right of the UI, the status should now be hacked

click.jpg

  1. If victim is an administrator now the user on the instance with ID 4 will also be an admin.
Setting up a mock server

To host your own mock server follow these steps

  1. Add these lines to the .htaccess file in the root directory
RewriteCond %{REQUEST_URI} ^/api/0/projects/test/test/issues/  
RewriteRule .* /sentry/list.json [L]  
RewriteCond %{REQUEST_URI} ^/api/0/projects/  
RewriteRule .* /sentry/projects.json [L]  
  1. Upload the two files that I have submitted under a folder sentry

projects.json

errors.json

  1. You will now be able to add your server as a sentry server, click connect and be presented with a project Test / test. And be able to list the two fake errors

Impact

Valid arbitrary PUT request as victim user. There is no restriction on what type of PUT request can be sent.

Examples:

  • Make admin PUT /users/:id
  • Elevate membership PUT /groups/:id/members/:user_id/state
  • Approve member to a group PUT /groups/:id/members/:member_id/approve
  • Change visibility PUT /groups/:id

And much more

What is the current bug behavior?

Error IDs are not sanitized or validated and can contain arbitrary values.

What is the expected correct behavior?

The id value should be restricted, sanitized or validated.

Output of checks

This bug happens on GitLab.com

Impact

Valid arbitrary PUT request as victim user. Can be used to elevate user to admin, change visibility of projects, accept access requests on groups/projects and much more. There is no restriction on what type of PUT request can be sent.

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section: