CVE-2021-30465: runc symlink-exchange race during mount setup ("symlink-exchange attack")
Scope: audit · README · COMPARISON · docs/RESEARCH
CVSS 8.5. Fixed in runc 1.0.0-rc95.
Source
Relevance to doos
runc resolved a mount target, then mounted it, with a window between the two where an attacker sharing a volume could swap the target for a symlink, redirecting the mount outside the container's intended root.
doos builds its own container directories with fsutil::mkdirs, which calls
std::filesystem::create_directories. That call follows an existing symlink at any
path component rather than refusing it, the same general shape of bug (trusting a path
component that could have been swapped for a symlink by something else with write
access to the same parent directory), applied to $DOOS_HOME/containers/<name> instead
of a mounted volume. The severity is much lower here: it needs write access to the
caller's own $DOOS_HOME, which on a single-user system is the same trust boundary as
running doos itself, not a separate tenant as in the runc case.
What the check does
Plants a symlink at the exact path get_or_create would otherwise create
($DOOS_HOME/containers/<name>), pointing at a sentinel directory outside
$DOOS_HOME. Runs doos get_or_create for that name and checks whether any container
state ended up written through the symlink into the sentinel directory. Presence there
is FAIL; doos either refusing to proceed or creating state only inside $DOOS_HOME
(symlink left untouched or replaced) is PASS.
Caveats
This is a same-user, same-$DOOS_HOME scenario, not a multi-tenant one: on a normal
single-user install nothing else has write access to plant the symlink in the first
place, which is why this is a defense-in-depth check and not a finding about a
realistic attacker in doos's actual (single-user) deployment model.
Run: make test in this directory, or make -C audit test from the repo root.