XSS in Maven Dependency Proxy on .com with CSP bypass for Firefox (bypass)
HackerOne report #3016600 by joaxcar
on 2025-02-27:
Report | Attachments | How To Reproduce
Report
Summary
The patch here
https://about.gitlab.com/releases/2025/02/26/patch-release-gitlab-17-9-1-released/#xss-in-maven-dependency-proxy
fixed most issues in #2939833 but when playing with the patch I found two additional CSP directives
that can cause XSS.
the patch looked like this
CSP_DIRECTIVES = "default-src 'none'; script-src 'none'; connect-src 'none'; img-src 'none';" \
"style-src 'none'; frame-ancestors 'none'; form-action 'none'"
RESPONSE_HEADERS = {
# from https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#basic-non-strict-csp-policy
# but with all directives set to none
'Content-Security-Policy' => CSP_DIRECTIVES,
'X-Content-Type-Options' => 'nosniff'
}.freeze
When looking at that linked resource for a "safe CSP" I found this list of CSP directives
https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#fetch-directives
containing
script-src specifies the locations from which a script can be executed from. It is a fallback directive for other script-like directives.
script-src-elem controls the location from which execution of script requests and blocks can occur.
script-src-attr controls the execution of event handlers.
It turns out that script-src-attr
and script-src-elem
will trump script-src
if they exists, just as script-src
will trump default-src
if it exists.
This makes it possible to force gitlab to cache a response with this content (you can replace elem
with attr
as well)
Content-Security-Policy: default-src 'none'
Content-Type: multipart/x-mixed-replace
--TEST
Content-Type: text/html
Content-Security-Policy: script-src-elem 'unsafe-inline'
<script>alert(document.domain)</script>
--TEST--
And have it trigger XSS in firefox when visited.
I have an example payload cached here (open in firefox):
https://gitlab.com/api/v4/projects/66166935/dependency_proxy/packages/maven/new/bypass
Steps to reproduce
- Host this script on a server reachable from the internet
- Create a new Ultimate trial on Gitlab.com
- Create a new project in the new Ultimate group, take a note of the project ID
- Go to https://gitlab.com/GROUPNAME/PROJECTNAME/-/settings/packages_and_registries
- Enable
Dependency Proxy
and enter your URL from step 1 in the URL input. Leave the other fields blank. Click save - Now go to https://gitlab.com/-/user_settings/personal_access_tokens and create a new
api
access token - Run this command in a terminal (replace
<USERNAME>
,<TOKEN>
and<PROJECT_ID>
curl -X GET -k --fail-with-body --verbose "https://<USERNAME>:<TOKEN>[@]gitlab.com/api/v4/projects/<PROJECT_ID>/dependency_proxy/packages/maven/new/bypass"
- This will cache the response
- Visit this in firefox (replace
<PROJECT_ID>
):https://gitlab.com/api/v4/projects/<PROJECT_ID>/dependency_proxy/packages/maven/new/bypass
and an XSS should pop
Impact
XSS on Gitlab.com with Firefox CSP bypass
What is the current bug behavior?
The CSP is still not strong enough on the proxy endpoint even if some attacks are now mitigated such as form injection.
What is the expected correct behavior?
The response should not render from cache in browsers with the unsafe combination of CSP and content-type
Output of checks
This bug happens on GitLab.com
Fix
I think the idea is good, and adding these two CSP directives
should do the trick. But an easier even more safe approach would be to add the CSP directive sandbox
to the response. This is something that GitHub for example do on their user-supplied content
Impact
XSS on Gitlab.com with Firefox CSP bypass
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section: