One Annotation Beats Every Flag: Podman’s CVSS 10.0 Sandbox Bypass Was a 2022 Feature, Found by Claude
On 29 September 2026 the Podman maintainers shipped v5.8.8 and v6.1.3 and published GHSA-2cvf-wqm6-wr9g. The CVE record, CVE-2026-94603, reached NVD on 13 October 2026 at 17:17 UTC carrying a CVSS 3.1 base score of 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H, scored by the GitHub CNA, CWE-693, status Received). The summary line the maintainers wrote for it is unusually blunt: “The podman run command can be instructed to disable almost all sandboxing — including user-requested sandboxing — by image annotation.”
The mechanism needs one line to state. If an OCI image carries the annotation io.podman.annotations.checkpoint.runtime.name, Podman treats it as a CRIU checkpoint rather than an image, and restores it — with the security context baked into the checkpoint, not the one the operator typed. podman run --cap-drop=ALL against such an image drops nothing. Seccomp, SELinux, and AppArmor settings go the same way. The image decides.
What makes this worth more than a patch note is the ancestry. This was not a bug. It was a feature, merged in December 2022, shipped in Podman 4.4.0 in February 2023, and covered by an end-to-end test that asserted the behaviour was correct for the next three and a half years. The fix is a revert.
The code path, and why the flags never arrived
We read the vulnerable source at tag v5.8.7 and the fixed source at v5.8.8. The defect lives in ContainerRun, and it was duplicated in both client paths: pkg/domain/infra/abi/containers.go for local execution and pkg/domain/infra/tunnel/containers.go for podman-remote. Both do the same thing. They inspect the image, look up define.CheckpointAnnotationRuntimeName in its annotations, and if present call ContainerRestore and return:
_, isCheckpointImage := imgData.Annotations[define.CheckpointAnnotationRuntimeName]
if isCheckpointImage {
var restoreOptions entities.RestoreOptions
restoreOptions.Name = opts.Spec.Name
restoreOptions.Pod = opts.Spec.Pod
responses, err := ic.ContainerRestore(ctx, []string{resolvedImageName}, restoreOptions)
Read what survives the transfer. Out of the entire SpecGenerator the user just built from the command line, exactly two fields are copied into the restore options: Name and Pod. Everything else is dropped on the floor — not overridden, not merged, not warned about. Simply never consulted.
The reason is structural, and it is the part worth internalising. The next statement after that block, in the non-checkpoint path, is generate.MakeContainer(...). That is where a user’s capability drops, seccomp profile, SELinux label, and the defaults from containers.conf are turned into an actual OCI runtime spec. The checkpoint branch returns before it runs. The flags were not evaluated and rejected; the function that evaluates them was skipped entirely. A control that lives downstream of an early return is not a weak control, it is an absent one.
The constant itself is defined in libpod/define/annotations.go and documented there as being set by podman container checkpoint “to specify the runtime used on the host where the checkpoint was created.” It was a provenance hint. It became an execution-mode switch, writable by anyone who can author an image manifest.
A feature from 2022, reverted in 2026
The commit the maintainers reverted is f4401567cdf6, authored 20 November 2022 by Radostin Stoyanov and merged as PR #16569, “Add support for checkpoint images with ‘podman run’,” on 7 December 2022. Its commit message is a worked example, not a security trade-off discussion:
This patch extends the podman run command with support for checkpoint images. When
podman runis invoked with an image that contains a checkpoint, it would restore the container from that checkpoint.
The intent was convenience: distribute a checkpoint through an OCI registry, pull it, run it. The advisory concedes the goal was reasonable and the conflation was not — “Conflating container images and checkpoints in podman run allows inherently insecure behavior as the image can now control how it is executed.”
That functionality first shipped in v4.4.0 on 1 February 2023, which is exactly where the advisory’s affected range begins. From that release until 29 September 2026 — a little under three years and eight months — every rootful podman run accepted an image-supplied instruction to ignore the operator’s sandbox.
Three commits per branch close it, all authored on 28 September 2026 by Matt Heon and cherry-picked across main, v6.1, and v5.8:
- The revert (
3721ff8/ea9243e/7728ba0, 11:48 UTC) removes 39 lines from the local path and 25 from the remote path. Its message states the reasoning in full: the feature “has been identified as a serious security issue” because “all security for a container, even options specified by the user at the command line, can be turned off by the checkpoint,” and that checkpoints “must be explicitly requested by the user to ensure they are used safely.” - The error (
2470629/450c644/c74b1d3, 13:26 UTC) adds a guard insideMakeContainerinpkg/specgen/generate/container_create.gothat returns"%s is a checkpoint and must be started using 'podman container restore' instead". The commit message flags the relocation deliberately: the check is now “done during Specgen processing, server side, which should be safer than the previous client-side processing for these.” The original annotation check ran in the client. Moving it behind the API boundary means an old or hostile client cannot simply decline to perform it. - The test (
fa6cef2/2d05a7c/29c8c6e, 14:28 UTC) is the one we would put on a slide. The existing case intest/e2e/checkpoint_image_test.gowas named"podman run with checkpoint image"and assertedExitCleanly()with one container running. It is renamed to"podman run with checkpoint image throws an error"and now assertsExitWithError(125, "is a checkpoint and must be started using 'podman container restore' instead"), with zero containers running. Fourteen lines of green assertions became one red one.
CI was passing the whole time. It was passing because the behaviour was intended. A test suite confirms that software does what its authors meant; it has nothing to say about whether what they meant is safe. This finding was reachable only by someone asking the second question.
The same fix also writes down what restore always meant
The error commit touches a second file, docs/source/markdown/podman-container-restore.1.md, adding six lines that are arguably the most useful output of the whole episode:
Please note that restoring a checkpoint will use the complete security context specified in the checkpoint. If a checkpoint specifies that it should be run with full capabilities added and all security features like Seccomp, SELinux, and Apparmor disabled, this will be done. Security configuration from containers.conf is not used, as the checkpoint must be restored with the originally-specified security configuration. As such, restoring a checkpoint is a security sensitive operation and should only be done on checkpoints that are fully trusted.
Note what this is not. It is not a change in behaviour. podman container restore worked this way before the CVE and works this way after it. The vulnerability was never that restore honours the checkpoint’s security context — that is the point of restore, and your site-wide containers.conf hardening does not apply to it either. The vulnerability was that an operator could reach restore semantics by typing run, with the image making the choice silently. The fix closes the implicit door and documents that the explicit one was always load-bearing.
Why this lands on an agent-security site
The dominant pattern for confining model-chosen code today is a container invocation assembled by the harness: podman run --rm --cap-drop=ALL --security-opt no-new-privileges --network=none, some image, some command. The entire safety argument rests on those flags being authoritative over the thing being run. CVE-2026-94603 is the clean counterexample — a case where the artefact being confined got to overrule the confinement, through metadata, without an error, without a warning line, and without any step the operator would recognise as having made a decision.
That matters more than usual in agent plumbing for one reason: the image reference is frequently not a constant. Harnesses that let a model, a task file, a devcontainer spec, or a pulled skill bundle choose which image to run have handed an untrusted input the one lever this bug exposes. We have covered the same shape repeatedly this month — the orb/sandboxed.sh nsenter namespace gap, where a missing --root left a “contained” process resolving paths against the host, and the deepseek-harness worker-thread realm escape, fixed by moving the boundary to the OS rather than hardening the VM. Podman is the most mature artefact in that list by a wide margin, and it had the same class of defect for three and a half years. The lesson generalises past any one runtime: enumerate every input that can change which code path builds your sandbox, not just the inputs that can change what runs inside it.
Two scoping details keep this from being a panic. Rootless containers are unaffected — checkpoint and restore are not supported rootless at all, so the branch is unreachable. And the CVSS 10.0 deserves a raised eyebrow: AV:N/PR:N/UI:N describes a remote, unprivileged, no-interaction attack, whereas the realistic path is an operator (or an automated pipeline) pulling and running an attacker-influenced image on a rootful host. The consequence is faithfully scored — scope change to full host-adjacent capability is real — but the vector reads as worst-case. Teams triaging by base score alone will rank this above things that are genuinely reachable from the internet; teams running rootful Podman in CI, in a build farm, or under an agent harness should rank it exactly where it sits.
Found by Claude, reported by humans
The advisory’s credits line reads: “This vulnerability was discovered by ADA Logics Ltd using Anthropic’s Claude models.”
It is worth being precise about what that sentence claims and what it does not. It credits a security consultancy that does sustained open-source auditing work, using a model as part of that process. It does not describe an autonomous system filing advisories. The triage, the reproduction, the severity argument, and the coordination with maintainers are human work, and the fix — reverting a four-year-old feature and accepting a breaking change in a patch release — is a maintainer judgement call no scanner makes.
Within those limits it is still a notable data point, and it is the kind we have been tracking since Anthropic opened its vulnerability-finding service to open-source projects. The bug has a shape that suits a reader rather than a fuzzer: no memory corruption, no crash, no anomalous input. Just a function that returns early, and a semantic question about whether the two code paths downstream of that branch are equivalent from a security standpoint. They are not, and nothing about the control flow looks wrong until you ask what MakeContainer was going to do. That is a reading comprehension problem over a large Go codebase, which is a reasonable thing to point a model at, and a poor fit for most of the tooling that has historically been pointed at Podman.
What to do
- Upgrade. The fix is in v5.8.8 and v6.1.3 (both 29 September 2026). The current releases as of this writing are v5.8.9 and v6.1.4, both published 12 October 2026, and both contain it — they also pull in gvproxy v0.9.0 for CVE-2026-107935, so there is no reason to stop at the security release.
- Expect the breaking change. Both release notes list the removal under Breaking Changes, not just Security. If any pipeline distributes checkpoints via a registry and starts them with
podman run, it will now fail with exit code 125. The supported replacement ispodman container restore, which is the same operation named honestly. - Treat
podman container restoreas a privileged operation. Per the new documentation, a restored checkpoint runs with the security context recorded in the checkpoint and yourcontainers.confhardening does not apply. Checkpoint integrity needs to be assured at creation, storage, retrieval, and restoration — the same chain of custody you would demand of a signed binary. - If you cannot upgrade yet, scan and reject. The advisory’s workaround is to check pulled images for the
io.podman.annotations.checkpoint.runtime.nameannotation and refuse them, and it is explicit that this is manual: “Podman does not presently have the ability to perform this scanning and rejection automatically.” An admission controller or registry-side policy check is the practical place to put it. - Prefer rootless where the workload allows. Not as a response to this CVE specifically, but because the reason rootless escaped is that it never had the feature — which is the usual reason reduced-privilege modes escape.
Verification note: we read the GitHub Security Advisory GHSA-2cvf-wqm6-wr9g directly for the summary, impact description, affected and patched version ranges, the nine fix commits, the workaround text, the rootless exemption, and the ADA Logics / Anthropic Claude credit line; the NVD 2.0 API record for CVE-2026-94603 (published 13 October 2026 17:17:49 UTC, last modified 13 October 2026 21:19:58 UTC, status Received, source security-advisories@github.com, CVSS 3.1 10.0 AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) and the OSV record for CWE-693 and the fix-commit list; the GitHub API for commits f4401567cdf6 (authored 20 November 2022, Radostin Stoyanov), PR #16569 (merged 7 December 2022), 3721ff82ffb4 and ea9243e0 and 7728ba0a (the revert, 28 September 2026 11:48 UTC), 24706290 and 450c6442 and c74b1d37 (the error and docs change, 13:26 UTC), and fa6cef22 and 2d05a7c6 and 29c8c6e5 (the test rework, 14:28 UTC), including full commit messages and diffs quoted above; and the release records for v4.4.0 (1 February 2023), v5.8.7, v5.8.8 (29 September 2026 16:25 UTC), v6.1.3 (29 September 2026 15:34 UTC), v5.8.9 and v6.1.4 (12 October 2026), with the Security and Breaking Changes notes quoted from the release bodies. The vulnerable code in pkg/domain/infra/abi/containers.go and pkg/domain/infra/tunnel/containers.go, the annotation constant in libpod/define/annotations.go, and the new guard in pkg/specgen/generate/container_create.go were read first-hand at tags v5.8.7 and v5.8.8. We did not build a checkpoint image, craft an annotated image, run any Podman command, or test the bypass; the mechanism described is read from source and from the maintainers’ own commit messages. Brave Search returned only CVE-mirror aggregators for this identifier and no analytical coverage; no secondary source was relied on for any claim above.
Sources:
- GitHub Security Advisory GHSA-2cvf-wqm6-wr9g — The
podman runcommand can be instructed to disable almost all sandboxing by image annotation (impact, workaround, rootless exemption, ADA Logics / Claude credit, 29 September 2026) - NVD — CVE-2026-94603 (published 13 October 2026, CVSS 3.1 10.0, CWE-693, status Received)
- Podman commit 3721ff8 — Revert “Enable ‘podman run’ for checkpoint images” (28 September 2026)
- Podman commit 2470629 — Error when a checkpoint image is run, plus the podman-container-restore documentation change (28 September 2026)
- Podman PR #16569 — Add support for checkpoint images with ‘podman run’ (merged 7 December 2022, the introducing change)
- Podman v5.8.8 release notes — Security and Breaking Changes (29 September 2026)
- Podman v6.1.3 release notes — Security and Breaking Changes (29 September 2026)