Mar 3, 2026·6 min read·1 visit
OpenClaw versions before 2026.1.29 are vulnerable to arbitrary file reads. Attackers can create malicious symbolic links in an agent's workspace (e.g., naming a link 'avatar.png' that points to '/etc/passwd'). The system follows these links during avatar resolution, exposing sensitive host files.
A symbolic link traversal vulnerability exists in the OpenClaw AI assistant platform, specifically within the agent avatar resolution logic. This flaw allows attackers with write access to an agent's workspace to create symbolic links pointing to arbitrary files on the host filesystem. When the gateway attempts to serve or process the agent's avatar, it follows the symbolic link and discloses the target file's contents, bypassing workspace isolation boundaries. The vulnerability also includes a Time-of-Check Time-of-Use (TOCTOU) race condition.
The vulnerability affects the OpenClaw gateway, a component responsible for managing AI agent sessions and serving user interface assets. OpenClaw assigns a local workspace directory to each agent where it can store files, including identity assets like avatars. The flaw resides in how the gateway resolves and serves these avatar images.
Specifically, the resolveIdentityAvatarUrl function and the avatar-serving endpoints failed to properly validate the destination of symbolic links. While the application verified that the requested file path string was nominally within the workspace directory, it did not resolve symbolic links before reading the file content. This allowed the filesystem APIs to transparently follow links pointing outside the restricted workspace root.
This is a classic Link Following vulnerability (CWE-59) combined with Improper Limitation of a Pathname to a Restricted Directory (CWE-22). It permits an attacker who can influence workspace content to read any file the OpenClaw process has permission to access, compromising the confidentiality of the host system.
The root cause is twofold: reliance on path string validation instead of canonical path validation, and the presence of a Time-of-Check Time-of-Use (TOCTOU) race condition.
1. Inadequate Path Validation:
The original implementation used path.resolve to normalize the requested file path and checked if the resulting string started with the workspace root path. However, standard path resolution logic in Node.js does not expand symbolic links. If an attacker created a link at /workspace/avatar.png pointing to /etc/passwd, the path string /workspace/avatar.png would arguably pass the containment check. When fs.readFileSync was subsequently called, the operating system resolved the link and returned the contents of /etc/passwd.
2. TOCTOU Race Condition:
The code performed a check using fs.statSync to verify file properties (such as size) before calling fs.readFileSync. These are distinct system calls. In the window between stat and read, an attacker could swap a valid image file with a symbolic link. The initial check would validate the safe file, but the read operation would target the malicious link, bypassing security controls.
The remediation involved shifting from path-based operations to file descriptor (FD) operations to eliminate race conditions and enforcing canonical path checks.
Vulnerable Logic (Conceptual):
// src/gateway/session-utils.ts (Pre-patch)
const avatarPath = path.join(workspaceRoot, 'avatar.png');
if (isPathWithinRoot(avatarPath, workspaceRoot)) {
// VULNERABLE: fs.stat followed by fs.readFile creates a race condition
// and fs.readFile follows symlinks by default.
const stats = fs.statSync(avatarPath);
if (stats.size < MAX_SIZE) {
const data = fs.readFileSync(avatarPath); // Follows symlink!
return createDataUrl(data);
}
}Patched Logic:
The fix introduces strict identity verification. The code now opens the file first to obtain a file descriptor, then performs checks on that specific descriptor. It also compares the file's identity (inode and device ID) from lstat (link-aware) and fstat (descriptor-aware) to ensure no swapping occurred.
// src/gateway/session-utils.ts (Patched)
import * as fs from 'fs';
// 1. Resolve the canonical path of the root to prevent traversal via root parents
const realRoot = fs.realpathSync(workspaceRoot);
// 2. Open file with O_NOFOLLOW to fail if the immediate path component is a link
// (Note: Node.js fs.openSync flags depend on platform, but logic enforces no-follow semantics)
const fd = fs.openSync(avatarPath, 'r');
try {
// 3. Verify file identity to prevent TOCTOU
const linkStats = fs.lstatSync(avatarPath);
const fdStats = fs.fstatSync(fd);
if (linkStats.ino !== fdStats.ino || linkStats.dev !== fdStats.dev) {
throw new Error('File identity mismatch (possible symlink swap)');
}
// 4. Verify canonical path containment
const realPath = fs.realpathSync(avatarPath);
if (!realPath.startsWith(realRoot)) {
throw new Error('Path traversal detected');
}
// 5. Safe read using the verified file descriptor
const buffer = Buffer.alloc(fdStats.size);
fs.readSync(fd, buffer, 0, fdStats.size, 0);
} finally {
fs.closeSync(fd);
}To exploit this vulnerability, an attacker must have the ability to create files within the agent's workspace. This is often achievable if the agent exposes file upload capabilities or if the attacker has compromised a lower-privileged component with workspace access.
Step 1: Workspace Access
The attacker gains access to the agent's workspace directory, typically located at ~/.openclaw/workspace/.
Step 2: Malicious Symlink Creation
The attacker deletes the legitimate avatar.png and replaces it with a symbolic link pointing to a sensitive system file.
# Target the system password file
ln -sf /etc/passwd ~/.openclaw/workspace/avatar.pngStep 3: Trigger Resolution
The attacker triggers an avatar refresh or navigates to the control interface where the avatar is displayed. The OpenClaw gateway receives a request to /avatar/{agentId} or calls resolveIdentityAvatarUrl internally.
Step 4: Data Exfiltration
The server reads the target of the symlink (/etc/passwd). If the endpoint returns a data URL, the attacker receives a Base64 string:
data:image/png;base64,cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaAo...
Decoding this string reveals the content of the compromised file.
This vulnerability poses a confidentiality risk. While it does not directly allow arbitrary code execution, the ability to read arbitrary files can lead to complete system compromise depending on the files accessible to the OpenClaw process.
Critical Assets at Risk:
config.json or .env files could expose API keys, database credentials, or third-party service tokens./etc/passwd (user enumeration) or SSH keys (if permissions allow) could facilitate lateral movement.The severity is mitigated by file permissions; the attacker can only read files that the user running the OpenClaw process can read. If OpenClaw runs as root (against best practices), the impact is critical. If it runs as a dedicated low-privilege user, the impact is limited to that user's access scope.
The vulnerability is fixed in OpenClaw version 2026.1.29. All users should upgrade immediately.
Primary Fix:
Upgrade to the patched version which implements fs.openSync with file descriptor checks and strict canonical path verification (fs.realpathSync) to prevent symlink traversal.
Defensive Configuration:
# Example audit command
find /path/to/openclaw/workspaces -type l -lsCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenClaw OpenClaw | < 2026.1.29 | 2026.1.29 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-59 (Link Following) |
| Attack Vector | Local / Remote (via Workspace) |
| CVSS Score | 6.6 (Medium) |
| Impact | Confidentiality Loss (Arbitrary File Read) |
| Exploit Status | PoC Available |
| Platform | Node.js / TypeScript |
Improper Link Resolution Before File Access ('Link Following')