AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

AppleGHSA-4587-w9mm-xxvh

OCI image load follows symlinks for `oci-layout` and `index.json` outside the extraction directory

Apple

Published Sep 9, 2026

Medium
Fix: upgrade to 0.45.0 or later (2 fixed versions below)
GitHub advisory

Impact

An attacker who can get a user to load a crafted OCI image archive (ImagesService.load(from:force:)) can cause the loader to import OCI metadata from attacker-chosen paths on the host, outside the archive's own extraction directory. This bypasses the trust boundary that image loading is supposed to enforce: everything the loader reads should come from the archive itself.

Details

ImagesService.load(from:force:) extracts the supplied tar archive into a temporary directory and then hands that directory to ImageStore.load(from:). ArchiveReader.extractEntry() rejects tar members whose *path* would escape the extraction directory, but it does not restrict where a *symlink entry's target* may point — a symlink named oci-layout or index.json can be created inside the extraction directory while pointing anywhere on the filesystem the process can reach.

LocalOCILayoutClient.loadIndexFromOCILayout(directory:) then reads oci-layout and index.json from that directory using ordinary filesystem calls, which transparently follow symlinks. A crafted archive can therefore redirect the OCI-loading stage to metadata located outside the extracted tree.

Affected code

  • ArchiveReader.extractEntry() — accepts symlink entries without validating their target.
  • LocalOCILayoutClient.loadIndexFromOCILayout(directory:) — reads oci-layout and index.json...

Affected versions

PackageAffectedFixed in
apple/container
Product
< 1.4.11.4.1
apple/containerization
Product
< 0.45.00.45.0
Details and references

### Impact An attacker who can get a user to load a crafted OCI image archive (`ImagesService.load(from:force:)`) can cause the loader to import OCI metadata from attacker-chosen paths on the host, outside the archive's own extraction directory. This bypasses the trust boundary that image loading is supposed to enforce: everything the loader reads should come from the archive itself. ### Details `ImagesService.load(from:force:)` extracts the supplied tar archive into a temporary directory and then hands that directory to `ImageStore.load(from:)`. `ArchiveReader.extractEntry()` rejects tar members whose *path* would escape the extraction directory, but it does not restrict where a *symlink entry's target* may point — a symlink named `oci-layout` or `index.json` can be created inside the extraction directory while pointing anywhere on the filesystem the process can reach. `LocalOCILayoutClient.loadIndexFromOCILayout(directory:)` then reads `oci-layout` and `index.json` from that directory using ordinary filesystem calls, which transparently follow symlinks. A crafted archive can therefore redirect the OCI-loading stage to metadata located outside the extracted tree. ### Affected code - `ArchiveReader.extractEntry()` — accepts symlink entries without validating their target. - `LocalOCILayoutClient.loadIndexFromOCILayout(directory:)` — reads `oci-layout` and `index.json` via normal filesystem access, following any symlink placed at those paths. ### Mitigations 1. Upgrade to the version of `apple/containerization` containing the fix. 2. Do not load OCI image archives from untrusted sources — an archive supplying `oci-layout`/`index.json` is part of the trust boundary, the same as any other image input. 3. If an untrusted archive has already been loaded on an affected version, treat the resulting image as potentially containing metadata and blobs imported from outside the archive rather than the archive's own contents, and remove it, then prune the local content store, since the imported content can persist there even if the image was never run or pushed: ```bash container image delete <ref> container image prune --all ``` ### Verifying whether you are affected Before loading an archive, check whether any of its top-level `oci-layout` or `index.json` entries are symlinks rather than regular files or directories: ```bash tar -tvf <archive.tar> | grep -E '^l.*(oci-layout|index\.json)' ``` If it has already been extracted, check the same paths in the extraction directory: ```bash find <extracted-dir> -maxdepth 1 \( -name oci-layout -o -name index.json \) -type l ```

Severity from
GitHub (reviewed advisory)

More Apple advisories

All Apple