CVE-2025-54867: Path Traversal in Youki Container Runtime
Welcome back to the security lab! Today, we're dissecting CVE-2025-54867, a crafty vulnerability in the youki
container runtime. It’s a classic tale of a trusted component being misled by a simple file system trick—a symbolic link—with potentially disastrous consequences. Grab your favorite beverage; we're going on a deep dive into how this container escape was possible and what we can learn from it.
TL;DR / Executive Summary
Vulnerability: CVE-2025-54867 is a path traversal vulnerability in the youki
container runtime caused by improper handling of symbolic links for /proc
and /sys
mount points.
Affected Software: youki
versions prior to 0.5.5
.
Impact: A malicious container image could use a symbolic link to trick the youki
runtime into mounting sensitive host resources or misconfiguring the container's environment. This could lead to a container escape, allowing an attacker to gain access to the underlying host system.
Severity: High. Container escape vulnerabilities fundamentally break the isolation model that makes containerization secure.
Mitigation: The fix is straightforward: upgrade youki
to version 0.5.5
or later. This version correctly validates that /proc
and /sys
are actual directories, not symbolic links, before mounting.
Introduction: The Trusting Gatekeeper
Imagine a container as a secure apartment in a large building (the host server). The container runtime is the building manager, responsible for giving each tenant (container) the right keys and ensuring they can't access their neighbors' apartments or the building's utility rooms. The /proc
and /sys
directories are like the apartment's own private fuse box and plumbing controls—they look and feel real but are virtualized and isolated from the main building's systems.
CVE-2025-54867 is what happens when a tenant leaves a deceptive note on their door: "For all deliveries, please go to the building's main electrical room." The building manager (youki
), without double-checking, follows the instruction and inadvertently gives the tenant control over something far more critical than their own apartment.
This vulnerability in youki
, a fast and efficient container runtime written in Rust, highlights a critical lesson: when building security boundaries, you can't trust the environment you're given. Let's explore how this simple symlink trick could unravel the isolation we depend on.
Technical Deep Dive: Following the Breadcrumbs
At its core, this vulnerability is a failure to validate the nature of a file path before performing a privileged operation on it. When youki
starts a container, one of its jobs is to mount special filesystems like procfs
and sysfs
inside the container at /proc
and /sys
, respectively.
Root Cause Analysis
The Open Container Initiative (OCI) specification dictates how runtimes should set up a container's environment, including its mounts. An attacker can craft a container image where the /proc
path isn't a directory as expected, but a symbolic link pointing somewhere else.
For example, an image could have /proc
as a symlink to /
.
# Inside a malicious Dockerfile or image build process
rm -rf /proc
ln -s / /proc
A vulnerable version of youki
would read the OCI spec, see a request to mount procfs
on /proc
, and proceed. However, because /proc
is a symlink to /
, the runtime would follow the link and attempt the mount operation on the target. This could lead to unpredictable behavior, but the most dangerous scenario is one that allows an attacker to influence paths outside the container's intended root filesystem, leading to a container escape.
The reference container runtime, runc
, had already addressed this and similar issues. It explicitly checks if /proc
and /sys
are directories before mounting. youki
, in its vulnerable state, skipped this crucial validation step.
Attack Vector and Business Impact
The primary attack vector is tricking a system into running a malicious container image. This is a significant threat in multi-tenant cloud environments, CI/CD pipelines, or any system that pulls and runs images from public or untrusted registries.
The impact is severe:
- Container Escape: The ultimate goal. An attacker could break out of the container's isolation and gain code execution on the host.
- Privilege Escalation: Once on the host, the attacker could potentially escalate privileges to root.
- Full System Compromise: With host access, an attacker can access data from other containers, attack the internal network, and persist their presence.
Proof of Concept (PoC)
We can demonstrate this vulnerability by simulating the conditions. This PoC is based on the test cases added in the patch that fixed CVE-2025-54867.
1. Prepare the Malicious Root Filesystem
First, we create a directory that will act as our container's root. Inside, we'll create the malicious symlink.
# Create a temporary directory for our container's rootfs
mkdir /tmp/malicious-rootfs
cd /tmp/malicious-rootfs
# Create a target directory for the symlink
mkdir some-other-dir
# Instead of a 'proc' directory, create a symlink named 'proc'
# pointing to our target directory.
ln -s ./some-other-dir ./proc
# Now, our rootfs has a /proc that is not a directory.
ls -l
# total 4
# lrwxrwxrwx 1 user user 15 Aug 15 10:00 proc -> ./some-other-dir
# drwxr-xr-x 2 user user 4096 Aug 15 10:00 some-other-dir
2. Define the Container Spec
Create a minimal config.json
that tells the runtime to mount /proc
.
{
"ociVersion": "1.0.2-dev",
"process": {
"args": ["/bin/sh"]
},
"root": {
"path": "/tmp/malicious-rootfs",
"readonly": true
},
"mounts": [
{
"destination": "/proc",
"type": "proc",
"source": "proc"
}
]
}
3. Execution
- With Vulnerable
youki
(< 0.5.5): The container creation would succeed. Theprocfs
would be mounted inside/tmp/malicious-rootfs/some-other-dir
instead of failing, demonstrating the symlink was followed. - With Patched
youki
(>= 0.5.5): The container creation would fail with an error, as the runtime now checks the destination path.
# youki --root /run/youki create my-container -b .
# Expected error on patched version:
# Error: failed to setup mount: filesystem proc must be mounted on ordinary directory
This simple check prevents the entire attack chain.
Mitigation and Remediation
Patching is your best friend here.
Immediate Fix:
Upgrade youki
to version 0.5.5 or later. You can typically do this through your package manager or by downloading the latest release from the official repository.
Patch Analysis (The Good Stuff):
The fix, committed in 0d9b4f2aa5ceaf988f3eb568711d2acf0a4ace37
, is located in crates/libcontainer/src/rootfs/mount.rs
. Let's look at the key logic.
// Simplified snippet from the patch
Some(typ @ ("proc" | "sysfs")) => {
// ... constructs the full destination path inside the container rootfs ...
let dest_path = options
.root
.join_safely(Path::new(mount.destination()).normalize())?;
// THE FIX: Check the metadata of the destination path
match fs::symlink_metadata(&dest_path) {
// If it exists and is NOT a directory, it's an error.
// This catches symlinks, files, etc.
Ok(m) if !m.is_dir() => {
return Err(MountError::Other(
format!("filesystem {} must be mounted on ordinary directory", typ)
.into(),
));
}
// If we get an error other than "Not Found", something is wrong.
Err(e) if e.kind() != ErrorKind::NotFound => {
return Err(MountError::Other(
format!("symlink_metadata failed for {}: {}", dest_path.display(), e)
.into(),
));
}
// Otherwise, it's either a directory or doesn't exist yet, which is fine.
_ => {}
}
// ... proceed with mounting ...
}
The code now uses fs::symlink_metadata
, which crucially does not follow symbolic links. It inspects the link itself. If the path exists and isn't a directory, the process halts. This elegantly shuts down the vulnerability by enforcing the simple assumption that /proc
and /sys
must be directories before they are used as mount points.
Timeline
- Discovery: Early August 2025 (Estimated)
- Vendor Notification: Early August 2025 (Estimated)
- Patch Committed: August 13, 2025 (Based on commit history)
- Public Disclosure (CVE Published): August 14, 2025
Lessons Learned
This CVE is a fantastic reminder of some fundamental security principles.
-
Never Trust Your Inputs: A container image is an input. The runtime should treat its filesystem layout with suspicion. Critical paths like
/proc
,/dev
, and/sys
demand rigorous validation before any privileged operations are performed on them. -
The Principle of Least Astonishment: Security-critical operations should behave predictably. Following a symlink when a directory is expected is an "astonishing" behavior that creates a security gap. The fix makes the behavior predictable and safe.
-
Learn from History: This exact type of bug was previously discovered and fixed in
runc
. When implementing security-critical software, it's vital to study the pitfalls and patches of existing, mature projects in the same domain.
Key Takeaway: Assumptions are the enemy of security. The assumption that a path in a filesystem is of a certain type (e.g., a directory) is a dangerous one. Always check, then act.
So, the next time you're working with file paths at a security boundary, ask yourself: "What if this isn't a directory? What if it's a symlink to /
? What if it's a FIFO pipe?" Thinking like an attacker is half the battle.
References and Further Reading
- Official GitHub Advisory: GHSA-j26p-6wx7-f3pw
- Youki Project Repository: https://github.com/youki-dev/youki
- Related
runc
Advisory: GHSA-fh74-hm69-rqjw - Fixing Commit in
youki
: 0d9b4f2aa5ceaf988f3eb568711d2acf0a4ace37