Stored XSS on Multi-word milestone reference leads to Admin-privilege escalation in certain conditions
HackerOne report #1455036 by ryhmnlfj on 2022-01-20, assigned to @mhenriksen:
Report | Attachments | How To Reproduce
Report
Summary
I found Stored XSS on Multi-word milestone reference in Markdown field.
Admin-privilege escalation can also be achieved by combining this XSS and CSP-bypass at CVE-2021-22242 under certain conditions.
However, the reproduction steps has become too long, so I submit the procedure that does not bypass CSP as the first report for convenience of confirmation and mitigation.
After submitting this first report, I will add the additional report with CSP-bypass and Admin-privilege escalation scenario as comments.
Steps to reproduce (Non-CSP-bypass)
Victim-side 1st steps
- Sign in to GitLab instance as victim-user.
- Create a new public project that everyone can create issue.
- After creating the new issue, log out of this victim's account and start Attacker-side steps.
Attacker-side steps
- 
Sign in to GitLab instance as attacker-user. 
- 
Create a new private project. 
- 
After creating a private project, make a note of the [FULL_NAMESPACE]from the URL displayed in the address bar of browser.
- 
Go to "Issues > Milestones" page and click "New milestone" button. 
- 
Create a new milestone with the following Title: 
New Milestone <script>alert(document.domain)</script>  - 
Go to the "Issue" page in the victim's public project prepared at Victim-side 1st step 2. 
- 
Prepare the description of new issue by replacing the string below. 
 Replace[FULL_NAMESPACE]with the values you got in Attacker-side step 3.
[FULL_NAMESPACE]%"New Milestone <script>alert(document.domain)</script>"  After replacing, create a new issue with this description.
- Log out of this attacker's account and start Victim-side 2nd steps.
Victim-side 2nd steps
- Sign in to GitLab as victim-user.
- Go to the issue page created at Attacker-side step 7. The XSS will be executed automatically.
What is the current bug behavior?
There are almost no restrictions on the regular expression that extract String-based multi-word milestone.
https://gitlab.com/gitlab-org/gitlab/-/blob/v14.6.3-ee/app/models/milestone.rb#L63-80
  def self.reference_pattern  
    # NOTE: The iid pattern only matches when all characters on the expression  
    # are digits, so it will match %2 but not %2.1 because that's probably a  
    # milestone name and we want it to be matched as such.  
    [@]reference_pattern ||= %r{  
      (#{Project.reference_pattern})?  
      #{Regexp.escape(reference_prefix)}  
      (?:  
        (?<milestone_iid>  
          \d+(?!\S\w)\b # Integer-based milestone iid, or  
        ) |  
        (?<milestone_name>  
          [^"\s\<]+\b |  # String-based single-word milestone title, or  
          "[^"]+"      # String-based multi-word milestone surrounded in quotes  
        )  
      )  
    }x  
  end  This regex means that injection is possible with a similar approach to CVE-2020-13338.
          "[^"]+"      # String-based multi-word milestone surrounded in quotes  What is the expected correct behavior?
I recommend limiting the regular expression that extract Multi-word milestone to the same extent as Single-word milestone.
Relevant logs and/or screenshots
I put references to the screenshots at the appropriate places in this report.
Output of checks
This bug happens on the official Docker installation of GitLab Enterprise Edition 14.6.3-ee.
I used Chromium 96 and Firefox 91 on Xubuntu 20.04 LTS to verify XSS.
I think it can be reproduced on gitlab.com by using CSP-bypassable vector, but I haven't tried it to prevent unexpected accidents.
Results of GitLab environment info
Output of sudo gitlab-rake gitlab:env:info:
System information  
System:		  
Proxy:		no  
Current User:	git  
Using RVM:	no  
Ruby Version:	2.7.5p203  
Gem Version:	3.1.4  
Bundler Version:2.1.4  
Rake Version:	13.0.6  
Redis Version:	6.0.16  
Git Version:	2.33.1.  
Sidekiq Version:6.3.1  
Go Version:	unknown
GitLab information  
Version:	14.6.3-ee  
Revision:	6360adc49de  
Directory:	/opt/gitlab/embedded/service/gitlab-rails  
DB Adapter:	PostgreSQL  
DB Version:	12.7  
URL:		http://mygitlab.local  
HTTP Clone URL:	http://mygitlab.local/some-group/some-project.git  
SSH Clone URL:	git@mygitlab.local:some-group/some-project.git  
Elasticsearch:	no  
Geo:		no  
Using LDAP:	no  
Using Omniauth:	yes  
Omniauth Providers: 
GitLab Shell  
Version:	13.22.1  
Repository storage paths:  
- default: 	/var/opt/gitlab/git-data/repositories  
GitLab Shell path:		/opt/gitlab/embedded/service/gitlab-shell  
Git:		/opt/gitlab/embedded/bin/git  Impact
This Stored XSS can be placed in the various Markdown field, and by using cross-project reference and Multi-word milestone in combination,
the attack target can be easily spread to public project owners and maintainers.
A trial calculation of CVSS in this standard scenario is as follows:
CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L  
8.9 (High)  After researching for scenarios that extend the attack to admin, I have found that it is possible to attack the admin in impersonation mode.
Admin-Privilege escalation can be achieved by combining a request to stop impersonation mode with a request to User modification API.
Let's assume a scenario where this impersonation mode is often used by admin of GitLab instance and the attack is successful.
Another trial calculation of CVSS is as follows:
CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H  
9.0 (Critical)  Though I think the impersonation mode is an interesting feature, I'm not sure if it's commonly used.
If it doesn't seem to be used much, please evaluate it as a standard scenario.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
- Full-namespace-of-private-project.png
- Create-new-milestone.png
- Create-new-issue_milestone.png
- XSS-result_milestone.png
How To Reproduce
Please add reproducibility information to this section:



