Skip to content
GHSA-wp6x-29qx-fpr7

GHSA-wp6x-29qx-fpr7

github.com • September 29, 2026

Brig mounts the project you name on the run line read-write at /work/ . It passed that path to the runtime as it was typed, without resolving it. The runtime resolves the path itself, on the host, outside the sandbox.

Every component at or below the project is the sandbox's to replace, because the project is mounted read-write. An agent can therefore replace a subdirectory with a symlink to any other directory on the host. The link does nothing inside the run that plants it: in the guest namespace it dangles, and the agent gains no access it did not already have.

It takes effect on the run that names a path through it. The operator narrows a run to that subdirectory, which is the ordinary way to point an agent at one part of a repository. Brig hands the unresolved path to the runtime, the runtime follows the link, and the directory the link points at is exported read-write into the sandbox.

The sandbox then reads and writes a host directory it chose, with the privileges of the user running Brig. ~/.ssh , ~/.aws , ~/.config/gh/hosts.yml , Brig's own state, and the guest of every other session are all reachable that way. Write access to ~/.ssh/authorized_keys or to a shell startup file makes it code execution as that user.

The path the operator types is their own, and the link is not visible in it. The PROJECT row in the run envelope printed the path as typed, so an operator who checked the envelope before the run was shown the name they had just typed rather than the directory it led to.

This does not cross the hypervisor or the guest-kernel boundary. Both hold throughout. Brig asks the runtime to export the directory, so the isolation the sandbox provides is not what fails here. It is a link-resolution flaw in Brig, with Brig acting as a confused deputy, and BRIG_CONTAINERD_RUNTIME=runc is not involved. --network isolated and --network offline do not affect it, because the access is a filesystem and not a network path.

The project is now reached the way the guest already was. The path is split where the first directory the invoking user can write appears. Above the split the entries are not the sandbox's to change, so that part is opened by name and the links the system put there keep working. From there down Brig descends one component at a time against the directory it already holds, refuses every symlink, and confirms after each open that what it opened is what it looked at. The boot descends a second time and holds the directory open across the handover to the runtime, because the is a string the runtime resolves on its own schedule.

A symlink anywhere below the split is refused, not only one at the directory being named. A link the operator created is refused as well: Brig cannot distinguish it from a planted one, so the rule is links rather than who made them, and the refusal names the target so it can be typed instead.

The PROJECT row now reports the directory the run resolved to rather than the path as typed.

Until then, resolve the project path yourself and name the resolved directory, which leaves no link for the runtime to follow:

Checking the directory you name is not sufficient on its own, because the link can sit at any component of the path. Treat any project whose path passes through a directory a sandbox has had mounted read-write as suspect, and resolve the whole path rather than inspecting its last component.

Reported by Peyton Kennedy of Endor Labs ( @p80n-sec ), who supplied a complete reproduction and the analysis of the two-run sequence that makes it reachable.

Extracted Entities

Attack Types (1)

Companies (1)

CWE Weaknesses (1)