Skip to content

User enumeration vectors /exists and /api/v4/users missing rate-limit

Summary

The permissions for some of our API endpoints make it too easy to mass-probe accounts once a potentially valid list of targets has been obtained.

This makes a scenario such as that detailed in https://about.gitlab.com/2019/05/14/git-ransom-campaign-incident-report-atlassian-bitbucket-github-gitlab/ [1] much more likely and easier to carry out.

https://www.owasp.org/index.php/Testing_for_User_Enumeration_and_Guessable_User_Account_(OWASP-AT-002) [2]

By virtue of # 1, # 2 shown below and https://gitlab.com/gitlab-org/gitlab-ce/issues/62493 it's easy for a malicious user to leverage GitLab as a source for valid user-data or a verify an already existing dataset.

/cc

@jritchey @estrike @jbroullon @dappelt for appsec feedback

@jarv @dawsmith for prod feedback

@northrup for infra feedback

@asaba for your involvement in [1]

@gitlab-com/gl-security/secops for visibility

1 /exists

Endpoint Authentication
https://gitlab.com/users/jsalazar-gitlab/exists none
{"exists":true}

2 /api/v4/users/id

Endpoint Authentication
https://gitlab.com/api/v4/users/33848217 none

For one user this would be the result:

https://gitlab.com/api/v4/users/33848217

```{"id":3355217,"name":"Jayson Salazar","username":"jsalazar-gitlab","state":"active","avatar_url":"https://assets.gitlab-static.net/uploads/-/system/user/avatar/3355217/avatar.png","web_url":"https://gitlab.com/jsalazar-gitlab","created_at":"2019-11-05T23:55:39.862Z","bio":"Security Engineer, Security Operations","location":"test","public_email":"","skype":"test","linkedin":"https://linkedin.com/in/jaysonsalazar","twitter":"test","website_url":"test","organization":"Gitlab Inc."}```

This endpoint can be iterated over. For multiple users the responses would look as follows:

shell:~/Desktop/WORK/gitlap.com$ for i in `seq 1 10000`; do echo -n $i ' '; curl -si https://gitlab.com/api/v4/users/$i | grep '{'; done
1  {"id":1,"name":"Si...}
2  {"message":"404 User Not Found"}
3  {"id":3,"name":"Tr...}
4  {"message":"404 User Not Found"}
5  {"id":5,"name":"Ch...}
6  {"id":6,"name":"ri...}
7  {"message":"404 User Not Found"}
8  {"message":"404 User Not Found"}
9  {"message":"404 User Not Found"}
10  {"message":"404 User Not Found"}
11  {"id":11,"name":"Bi...}
12  {"message":"404 User Not Found"}
13  {"message":"404 User Not Found"}
14  {"id":14,"name":"Ka...
15  {"id":15,"name":"JJ...
16  {"message":"404 User Not Found"}
17  {"message":"404 User Not Found"}
18  {"id":18,"name":"sw...}
19  {"message":"404 User Not Found"}
20  {"id":20,"name":"Gö...
21  {"message":"404 User Not Found"}
22  {"message":"404 User Not Found"}
23  {"message":"404 User Not Found"}
24  {"id":24,"name":"Ph...}

Some sort of rate-limit seems to be in place but is, for security purposes at least, ineffective.

shell:~$ for i in `seq 1 10000`; do echo -n $i ' '; curl -si https://gitlab.com/api/v4/users/$i | grep RateLimit-Remaining; done
1  RateLimit-Remaining: 598
2  RateLimit-Remaining: 599
3  RateLimit-Remaining: 598
4  RateLimit-Remaining: 597
5  RateLimit-Remaining: 599
6  RateLimit-Remaining: 598
7  RateLimit-Remaining: 597
8  RateLimit-Remaining: 596
9  RateLimit-Remaining: 599
10  RateLimit-Remaining: 598
11  RateLimit-Remaining: 597
Edited by Jayson Salazar Rodriguez