@@ -767,3 +767,354 @@ In the example above, the `is_admin?` method is overwritten when passing it to t
- If you must, be **very** confident that you've sanitized the values correctly.
Consider creating an allowlist of values, and validating the user input against that.
- When extending classes that use metaprogramming, make sure you don't inadvertently override any method definition safety checks.
## Working with archive files
Working with archive files like `zip`, `tar`, `jar`, `war`, `cpio`, `apk`, `rar` and `7z` presents an area where potentially critical security vulnerabilities can sneak into an application.
### Zip Slip
In 2018, the security company Snyk [released a blog post](https://snyk.io/research/zip-slip-vulnerability) describing research into a widespread and critical vulnerability present in many libraries and applications which allows an attacker to overwrite arbitrary files on the server file system which, in many cases, can be leveraged to achieve remote code execution. The vulnerability was dubbed Zip Slip.
A Zip Slip vulnerability happens when an application extracts an archive without validating and sanitizing the filenames inside the archive for directory traversal sequences that change the file location when the file is extracted.
Example malicious file names:
-`../../etc/passwd`
-`../../root/.ssh/authorized_keys`
-`../../etc/gitlab/gitlab.rb`
If a vulnerable application extracts an archive file with any of these file names, the attacker can overwrite these files with arbitrary content.
### Insecure archive extraction examples
#### Ruby
For zip files, the [rubyzip](https://rubygems.org/gems/rubyzip) Ruby gem is already patched against the Zip Slip vulnerability and will refuse to extract files that try to perform directory traversal, so for this vulnerable example we will extract a `tar.gz` file with `Gem::Package::TarReader`:
Always expand the destination file path by resolving all potential directory traversals and other sequences that can alter the path and refuse extraction if the final destination path does not start with the intended destination directory.
##### Ruby
```ruby
# tar.gz extraction example with protection against Zip Slip attacks.
raise"filename is outside of destination directory"unless
destination.start_with?(destination_dir+"/"))
destination
end
```
```ruby
# zip extraction example using rubyzip with built-in protection against Zip Slip attacks.
require'zip'
Zip::File.open("/tmp/uploaded.zip")do|zip_file|
zip_file.eachdo|entry|
# Extract entry to /tmp/extracted directory.
entry.extract("/tmp/extracted")
end
end
```
##### Go
You are encouraged to use the secure archive utilities provided by [LabSec](https://gitlab.com/gitlab-com/gl-security/appsec/labsec) which will handle Zip Slip and other types of vulnerabilities for you. The LabSec utilities are also context aware which makes it possible to cancel or timeout extractions:
Symlink attacks makes it possible for an attacker to read the contents of arbitrary files on the server of a vulnerable application. While it is a high-severity vulnerability that can often lead to remote code execution and other critical vulnerabilities, it is only exploitable in scenarios where a vulnerable application accepts archive files from the attacker and somehow displays the extracted contents back to the attacker without any validation or sanitization of symbolic links inside the archive.
### Insecure archive symlink extraction examples
#### Ruby
For zip files, the [rubyzip](https://rubygems.org/gems/rubyzip) Ruby gem is already patched against symlink attacks as it simply ignores symbolic links, so for this vulnerable example we will extract a `tar.gz` file with `Gem::Package::TarReader`:
STDERR.puts("archive file does not exist or is not readable")
exit(false)
end
tar_extract.rewind
# Loop over each entry and output file contents
tar_extract.eachdo|entry|
nextifentry.directory?
# Oops! We don't check if the file is actually a symbolic link to a potentially sensitive file.
putsentry.read
end
```
#### Go
```golang
// printZipContents INSECURELY prints contents of files in a zip file.
funcprintZipContents(srcstring)error{
r,err:=zip.OpenReader(src)
iferr!=nil{
returnerr
}
deferr.Close()
// Loop over each entry and output file contents
for_,f:=ranger.File{
iff.FileInfo().IsDir(){
continue
}
rc,err:=f.Open()
iferr!=nil{
returnerr
}
deferrc.Close()
// Oops! We don't check if the file is actually a symbolic link to a potentially sensitive file.
buf,err:=ioutil.ReadAll(rc)
iferr!=nil{
returnerr
}
fmt.Println(buf.String())
}
returnnil
}
```
#### Best practices
Always check the type of the archive entry before reading the contents and ignore entries that are not plain files. If you absolutely must support symbolic links, ensure that they only point to files inside the archive and nowhere else.
##### Ruby
```ruby
# tar.gz extraction example with protection against symlink attacks.
STDERR.puts("archive file does not exist or is not readable")
exit(false)
end
tar_extract.rewind
# Loop over each entry and output file contents
tar_extract.eachdo|entry|
nextifentry.directory?
# By skipping symbolic links entirely, we are sure they can't cause any trouble!
nextifentry.symlink?
putsentry.read
end
```
##### Go
You are encouraged to use the secure archive utilities provided by [LabSec](https://gitlab.com/gitlab-com/gl-security/appsec/labsec) which will handle Zip Slip and symlink vulnerabilities for you. The LabSec utilities are also context aware which makes it possible to cancel or timeout extractions.
In case the LabSec utilities do not fit your needs, here is an example for extracting a zip file with protection against symlink attacks:
```golang
// printZipContents prints contents of files in a zip file with protection against symlink attacks.
funcprintZipContents(srcstring)error{
r,err:=zip.OpenReader(src)
iferr!=nil{
returnerr
}
deferr.Close()
// Loop over each entry and output file contents
for_,f:=ranger.File{
iff.FileInfo().IsDir(){
continue
}
// By skipping all irregular file types (including symbolic links), we are sure they can't cause any trouble!