Skip to content

Privilege Escalation For Custom Role with Manage Group Member Permission Via Shared Group

⚠️ Please read the process on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.

HackerOne report #3200469 by rogerace on 2025-06-14, assigned to GitLab Team:

Report | How To Reproduce

Report

Summary

Via Sharing Group feature, a Developer with Manage Group Member permission can escalate privilege to a developer role that has all custom permissions granted, enabling him to manage runners, delete projects, and all other things allowed by all these custom permissions. See here for all custom permissions possible.

What is this Manage Group Member Permission

As we can see here, this custom permission allows:

Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role.

So basically, you can have developer role as base role, and have manage group member as addon permission (let's call this role dev_user), and then this developer can invite other users. But there is one limitation, that is this developer must select a role that has same or fewer permission than this custom role.

For example, if you try to invite another member as owner. Sorry, you cannot do.

What is tricky is what if there is another custom role that is also based on developer, but have all the other custom permission added. So this is a quite powerful developer, let's call it dev_all.

If you try to user our dev_user to invite a user and give it dev_all, you will find you cannot do it. The reason is simple: though they have the same base role, dev_all actually is much more powerful and have more permission than dev_user. It can manage runners, delete project, and so on.

But via a feature called Invite a group, we can achieve it, bypassing this access control, and privilege escalatge our dev_user to dev_all.

Steps to reproduce

You must use an ultimate self-managed Gitlab. We need two account: admin account and attacker_account.

Using admin account:

  1. Go to https://gitlab.example.com/admin/application_settings/roles_and_permissions, create dev_user, which has base role as developer and custom permission manage group member. Also create dev_all, which has base role as developer and all custom permissions added.
  2. Create a private group (victim group) and a private project inside it.
  3. Invite attacker_account to the private ==group==, give it dev_user role at https://gitlab.example.com/groups/VICTIM_GROUP_SLUG/-/group_members.

Using attacker_account:
4. Create a private group (attacker group)
5.Go to the the project we created in step 2 inside the victim group, you can see you CANNOT see Settings, which is expected since you are just a normal developer who can manage group members.
7. Go to https://gitlab.example.com/groups/VICTIM_GROUP_SLUG/-/group_members, where you can manage ==group== members (since you are dev_user).
8. On top right, click ==Invite a group==, select the group created in step 4 (attacker group) and ==select dev_all as maximum role==.
9. Finish the invite, and you can see your attacker_account in the victim group has privilege escalated to dev_all. To prove it, you can go to the private project we created in step 2, and see you can see the Settings, and can manage runners, can delete projects, and all sort of things.

Impact

PR: Low. We only need a developer that can manage group members
C: High, this is privilege escalation, and make this developer can see things like runners authentication token
I: High, this is privilege escalation, and make this developer can inject malicious runner to the group
A: High, this is privilege escalation, and this developer can delete runners and even delete the whole project.

How To Reproduce

Please add reproducibility information to this section: