GitLab’s issue email address never expires, and Aikido Security found whoever holds one can commit code to any branch the account can push to.
The address hides behind a button labeled “Email work item to this project,” and the middle of the string is a token bound to the whole account.
The scoping is an illusion: every address a user is given carries the same value, unlocking public and private repositories alike.
GitLab checks nothing about the sender: any mailbox can post to the address, and whatever arrives is treated as the account holder’s instruction.
Write a patch, name the target branch in the subject line, attach it and send; swap the suffix from -issue to -merge-request and the message opens a merge request instead of an issue. GitLab applies the patch, creating the branch if needed, and credits the commit to the account holder.
That bites when the patch edits .gitlab-ci.yml: the job GitLab runs belongs to the attacker and inherits the account’s permissions.
The token carries the holder’s own permissions only, so a Guest address is close to worthless while a Maintainer’s opens protected branches and CI secrets. Reaching one named project also takes its path and numeric ID, which public projects publish.
Incoming mail sidesteps IP allowlists and two-factor authentication too. Aikido showed it by locking a private project to one IP address, none of them its own: GitLab refused the browser and a git clone, yet the mail-borne merge request went through and the commit landed on main.
Resetting the token from the personal access tokens page kills every project address at once, and one still in use stops working until a replacement is issued. A self-managed administrator can shut the mail intake off instance-wide; no equivalent switch exists for one person’s account.
