A guest user is capable of bypassing API / UI restrictions and view dependency lists of private projects through job artifacts
HackerOne report #2189464 by ricardobrito
on 2023-10-02, assigned to @ngeorge1:
Report | Attachments | How To Reproduce
Report
Summary
A guest
user inside a group, by default is not allowed to view dependency lists, and if a guest
user tries to access the dependency lists inside a private project in a group either through the UI or the API, the access controls do not allow him to do so (this is properly implemented). However, I have found a way for a guest
user to access dependency lists of any private project of a group he is a member of, by using the job artifact that is created after the dependency scan.
Steps to reproduce
- As user A, create a public group, inside this group create a private project, say
project A
. - As user A, inside project A, create the following 2 files:
requirements.txt
view content
###
### This file is autogenerated by pip-compile with python 3.10
### To update, run:
###
### pip-compile
###
contourpy==1.0.6
# via matplotlib
cycler==0.11.0
# via matplotlib
fonttools==4.38.0
# via matplotlib
kiwisolver==1.4.4
# via matplotlib
matplotlib==3.6.2
# via
# -r requirements.in
# seaborn
numpy==1.23.5
# via
# -r requirements.in
# contourpy
# matplotlib
# pandas
# scipy
# seaborn
packaging==21.3
# via matplotlib
pandas==1.5.1
# via
# -r requirements.in
# seaborn
pillow==9.3.0
# via matplotlib
pyparsing==3.0.9
# via
# matplotlib
# packaging
python-dateutil==2.8.2
# via
# matplotlib
# pandas
pytz==2022.6
# via pandas
scipy==1.9.3
# via seaborn
seaborn==0.10.1
# via -r requirements.in
six==1.16.0
# via python-dateutil
.gitlab-ci.yml
view content
include:
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
build-job:
stage: build
script:
- echo "Hello, $GITLAB_USER_LOGIN!"
test-job1:
stage: test
script:
- echo "This job tests something new"
test-job2:
stage: test
script:
- echo "This job tests something, but takes more time than test-job1."
- echo "After the echo commands complete, it runs the sleep command for 20 seconds"
- echo "which simulates a test that runs 20 seconds longer than test-job1"
- sleep 20
deploy-prod:
stage: deploy
script:
- echo "This job deploys some stuff ( ... ) from the $CI_COMMIT_BRANCH branch."
environment: production
- As user A, commit the changes so that the pipeline may run
- Still as user A, go to the project path and on the left side meny go to ** secure-> dependency lists ** and you should see a list of dependencies that were found after the ci job completed:
- As user A, invite another user, say User B to the group with
guest
priviledges. - As user B, go to the project path and on the left side menu go to ** secure-> dependency lists ** and you will see the following:
This is correct behavior because the user only has guest
permissions, and this is a private prject (the user cannot even see the code).
- As user B, inside the private project, go to ** build -> jobs** and select the job with the name ** gemnasium-python-dependency_scanning ** and on the right-side under job artifacts select browse and you will be taken to a job artifact which is a json file that contains the dependency list:
view content
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"serialNumber": "urn:uuid:24dfaf3c-57c8-420f-b4ee-5f637f11228f",
"version": 1,
"metadata": {
"timestamp": "2023-10-02T13:23:03Z",
"tools": [
{
"vendor": "GitLab",
"name": "Gemnasium",
"version": "4.4.7"
}
],
"authors": [
{
"name": "GitLab",
"email": "support@gitlab.com"
}
],
"properties": [
{
"name": "gitlab:dependency_scanning:input_file",
"value": "requirements.txt"
},
{
"name": "gitlab:dependency_scanning:input_file:path",
"value": "requirements.txt"
},
{
"name": "gitlab:dependency_scanning:package_manager",
"value": "pip"
},
{
"name": "gitlab:dependency_scanning:package_manager:name",
"value": "pip"
},
{
"name": "gitlab:meta:schema_version",
"value": "1"
}
]
},
"components": [
{
"name": "Pillow",
"version": "9.3.0",
"purl": "pkg:pypi/Pillow@9.3.0",
"type": "library",
"bom-ref": "pkg:pypi/Pillow@9.3.0"
},
{
"name": "contourpy",
"version": "1.0.6",
"purl": "pkg:pypi/contourpy@1.0.6",
"type": "library",
"bom-ref": "pkg:pypi/contourpy@1.0.6"
},
{
"name": "cycler",
"version": "0.11.0",
"purl": "pkg:pypi/cycler@0.11.0",
"type": "library",
"bom-ref": "pkg:pypi/cycler@0.11.0"
},
{
"name": "fonttools",
"version": "4.38.0",
"purl": "pkg:pypi/fonttools@4.38.0",
"type": "library",
"bom-ref": "pkg:pypi/fonttools@4.38.0"
},
{
"name": "kiwisolver",
"version": "1.4.4",
"purl": "pkg:pypi/kiwisolver@1.4.4",
"type": "library",
"bom-ref": "pkg:pypi/kiwisolver@1.4.4"
},
{
"name": "matplotlib",
"version": "3.6.2",
"purl": "pkg:pypi/matplotlib@3.6.2",
"type": "library",
"bom-ref": "pkg:pypi/matplotlib@3.6.2"
},
{
"name": "numpy",
"version": "1.23.5",
"purl": "pkg:pypi/numpy@1.23.5",
"type": "library",
"bom-ref": "pkg:pypi/numpy@1.23.5"
},
{
"name": "packaging",
"version": "21.3",
"purl": "pkg:pypi/packaging@21.3",
"type": "library",
"bom-ref": "pkg:pypi/packaging@21.3"
},
{
"name": "pandas",
"version": "1.5.1",
"purl": "pkg:pypi/pandas@1.5.1",
"type": "library",
"bom-ref": "pkg:pypi/pandas@1.5.1"
},
{
"name": "pyparsing",
"version": "3.0.9",
"purl": "pkg:pypi/pyparsing@3.0.9",
"type": "library",
"bom-ref": "pkg:pypi/pyparsing@3.0.9"
},
{
"name": "python-dateutil",
"version": "2.8.2",
"purl": "pkg:pypi/python-dateutil@2.8.2",
"type": "library",
"bom-ref": "pkg:pypi/python-dateutil@2.8.2"
},
{
"name": "pytz",
"version": "2022.6",
"purl": "pkg:pypi/pytz@2022.6",
"type": "library",
"bom-ref": "pkg:pypi/pytz@2022.6"
},
{
"name": "scipy",
"version": "1.9.3",
"purl": "pkg:pypi/scipy@1.9.3",
"type": "library",
"bom-ref": "pkg:pypi/scipy@1.9.3"
},
{
"name": "seaborn",
"version": "0.10.1",
"purl": "pkg:pypi/seaborn@0.10.1",
"type": "library",
"bom-ref": "pkg:pypi/seaborn@0.10.1"
},
{
"name": "six",
"version": "1.16.0",
"purl": "pkg:pypi/six@1.16.0",
"type": "library",
"bom-ref": "pkg:pypi/six@1.16.0"
}
]
}
The contents are the same as the dependency list in the UI that the guest user was unable to access before.
The guest
user does not have permission to access the dependency list in the UI but using this bypass he can have access to a complete list of dependencies of private projects (which he can not view the content of).
POC video
dependency-list-access-control.mov
Impact
A guest user is capable of accessing the dependency list of a private project inside a group, even though it is not allowed (due to his low permissions), by using job artifacts.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section:
Possible fixes
- highlight the risk of exposing data when enabling public pipelines
- add an alert notification for users with private projects and public pipelines enabled
- update the permissions documentation
- Add permissions check to restrict access to data exposed in CI jobs and artifact similarly to what we do in the GitLab UI
- when browsing or downloading artifacts
- when displaying/downloading CI job log output?