Feb 17, 2026·7 min read·57 visits
Critical Path Traversal in OpenClaw's plugin installer allows attackers to write files to arbitrary locations via malicious package names. Fixed in v2026.2.1.
In the race to build the ultimate AI assistant, the developers of OpenClaw (formerly Moltbot) forgot one of the oldest rules in the book: never trust user input, especially when that input tells you where to write files. GHSA-QRQ5-WJGG-RVQW is a critical Path Traversal vulnerability that turns a helpful plugin installation process into an arbitrary file write primitive. By simply crafting a malicious 'package.json', an attacker can escape the application's sandbox and overwrite system files, leading to Remote Code Execution (RCE). It's a classic case of convenience over security, proving that even modern AI frameworks can suffer from 1990s-era bugs.
OpenClaw (previously known as Clawdbot and Moltbot) pitches itself as the ultimate open-source AI personal assistant framework. It's the kind of tool developers love: modular, extensible, and seemingly intelligent. It allows users to install "skills" (plugins) and "hooks" to expand its capabilities. Want your bot to check the weather? Install a weather plugin. Want it to manage your calendar? There's a hook for that.
But here's the catch: the mechanism that installs these plugins is trusting to a fault. When you install a plugin, the system has to decide where to put the files on your disk. A secure system would say, "I will put this in the plugins/ directory, labeled with a random ID or a sanitized name." OpenClaw, however, took a different approach. It essentially asked the plugin, "Hey, what's your name and where would you like to stay?"
This vulnerability isn't some complex heap overflow or a subtle race condition. It is a logic flaw in how the application processes file paths from the plugin's manifest (package.json). Because the application failed to realize that a name could contain directory traversal characters like .., it allowed malicious plugins to break out of their designated sandbox. It's the digital equivalent of inviting a guest into your home and telling them, "Go sleep in the room labeled 'Master Bedroom'," but they've labeled their suitcase "../Neighbor's House/Living Room."
The root cause of this vulnerability lies in the unscopedPackageName function and the subsequent file path construction. When a user runs a command like openclaw plugins install, the CLI reads the package.json of the target package to determine its identity. The logic was intended to strip npm scopes (e.g., changing @my-scope/my-plugin to my-plugin) to keep directory names clean.
However, the developers missed a critical edge case: what if the "name" isn't just a name? What if it's a path?
The vulnerable code essentially did this:
name from package.json.path.join(extensionsDir, theName).In Node.js, path.join is purely mechanical. It doesn't check if the resulting path is safe; it just concatenates strings using the OS separator. If the plugin name is something innocuous like weather-bot, the path becomes /app/extensions/weather-bot. But if an attacker sets the name to @evil/../../../../etc/cron.d, the scope stripper might return ../../../../etc/cron.d. When joined with /app/extensions/, the result resolves to /etc/cron.d. The application then proceeds to extract the plugin's contents into that directory.
This is a textbook Path Traversal (CWE-22). The application blindly trusted that the identifier provided by the package was a safe filename, not a relative path instruction.
Let's look at the difference between the naive implementation and the hardened code. The vulnerability existed in src/hooks/install.ts and src/plugins/install.ts.
The Vulnerable Logic (Conceptual):
// The code implicitly trusted the output of unscopedPackageName
const pluginName = getUnscopedName(packageJson.name);
const installDir = path.join(EXTENSIONS_DIR, pluginName);
// ... extract files to installDir ...If pluginName is ../../tmp, installDir becomes /tmp (depending on the base path). The installer creates the directory and dumps files there.
The Fix (Commit d03eca8450dc493b198a88b105fd180895238e57):
The fix introduced in v2026.2.1 is robust. It employs two strategies: sanitization and verification.
path.relative to ensure the final path is actually inside the target directory.function resolveSafeInstallDir(
extensionsDir: string,
pluginId: string,
): { ok: true; path: string } | { ok: false; error: string } {
// 1. Sanitize: Replace / and \ with __
const safeId = safeDirName(pluginId);
// 2. Resolve absolute paths
const targetDir = path.join(extensionsDir, safeId);
const resolvedBase = path.resolve(extensionsDir);
const resolvedTarget = path.resolve(targetDir);
// 3. Verify hierarchy using path.relative
const relative = path.relative(resolvedBase, resolvedTarget);
if (
!relative ||
relative === ".." ||
relative.startsWith(`..${path.sep}`) ||
path.isAbsolute(relative)
) {
return { ok: false, error: "invalid plugin name: path traversal detected" };
}
return { ok: true, path: targetDir };
}> [!NOTE]
> The use of path.relative here is the "gold standard" check. It calculates the relationship between the base and the target. If the relationship starts with .., it means the target has escaped the base. Simple, elegant, and secure.
To exploit this, we don't need buffer overflows or heap spraying. We just need a text editor and a malicious intention. The goal is to create a plugin that, when installed, writes a file to a sensitive location (like /etc/cron.d for persistence or ~/.ssh for access).
Step 1: Create the Malicious Manifest
Create a directory for your "plugin" and add a package.json. The trick is in the name field.
{
"name": "@innocuous/../../../../tmp/pwned",
"version": "1.0.0",
"description": "Just a harmless weather plugin...",
"main": "index.js"
}Step 2: Add the Payload
In the same directory, create the file you want to drop. If we want to drop a file named hack.sh into /tmp/pwned/, we just include it in the package structure.
Step 3: Pack and Install
Normally, plugins are installed from npm or a tarball. If we point the OpenClaw CLI to our local directory or a hosted tarball:
openclaw plugins install ./my-evil-pluginThe CLI reads the manifest, sees the name, calculates the path [base_dir]/../../../../tmp/pwned, and happily extracts our contents there.
The Result
If the application is running as root (which, terrifically, many Dockerized bots do), you just overwrote arbitrary files on the filesystem. If you overwrite a startup script or a cron job, you have achieved Remote Code Execution (RCE).
The impact of arbitrary file write cannot be overstated. In the context of an AI assistant, this is particularly dangerous because these bots often have access to sensitive data (API keys, calendars, emails) and run in environments with significant network access.
/etc/cron.d), systemd unit paths, or user profile scripts (.bashrc), an attacker gains full code execution on the next trigger.This isn't just about breaking the bot; it's about using the bot as a pivot point into the internal network. If the bot is running inside a corporate network, that file write is your beachhead.
If you are running OpenClaw (or Moltbot/Clawdbot), you need to update immediately. The vulnerability was patched in version 2026.2.1.
For Developers: This serves as a critical lesson in input validation. Never trust data just because it comes from a "manifest" file. Files inside a tarball or fields in a JSON file are just as untrusted as an HTTP request body.
../) alone; it is often bypassable.path.relative.For Ops/Admins:
extensions/ and hooks/ directories.root turns a file write bug into a system wipe bug. A bot running as nobody limits the blast radius significantly.CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenClaw OpenClaw | < 2026.2.1 | 2026.2.1 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Path Traversal |
| Attack Vector | Network (via Package Install) |
| CWE ID | CWE-22 |
| CVSS Score | 8.8 (High) |
| Impact | Arbitrary File Write / RCE |
| Fix Version | v2026.2.1 |
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
A high-severity Denial of Service (DoS) vulnerability in the S3 compatibility layer of rclone allows unauthenticated remote attackers (or authenticated attackers depending on configuration) to trigger rapid memory exhaustion and process termination. The flaw lies in the handling of S3 multipart uploads, where rclone eagerly allocates buffers based on untrusted size headers and fails to prevent integer overflows in its concurrent request admission control.
CVE-2026-88046 (also tracked via GHSA-38xv-hf3p-h7mq) is a directory traversal and root confinement escape vulnerability residing in the core listing and transfer logic of rclone. Prior to version 1.75.1, raw relative parent-directory sequences returned by flat-keyspace source backends are trusted and processed without proper sanitization, enabling writes outside the designated target root or bucket.
The FTP server implementation of rclone is vulnerable to a cross-session identity and credential confusion flaw when configured with an authentication proxy. Under specific multi-tenant configurations where multiple distinct sessions authenticate with the same username, a global map caches credentials globally instead of isolating them inside the session context. This allows a concurrent attacker to hijack the active session backend of a victim using the same username.
An authentication bypass vulnerability exists in rclone when dynamically starting FTP, S3, or SFTP servers via the Remote Control (RC) 'serve/start' API. The server constructors incorrectly check the global process configuration rather than request-scoped options, resulting in a silent bypass of the authentication proxy and enabling unauthenticated access.
Prior to version 1.75.1, rclone's S3 server component ('rclone serve s3') contains an authentication bypass vulnerability when configured with '--auth-proxy' but without '--auth-key'. The application validates AWS Signature Version 4 (SigV4) against an empty secret key string, enabling unauthenticated remote attackers to access storage backends.
A request-level denial of service vulnerability exists in rclone versions prior to 1.75.1 when configured with local symlink virtualization (--links) and serving files over HTTP or WebDAV. An unauthenticated remote attacker can trigger a Go runtime slice bounds panic by sending a crafted HTTP Range request with an offset exceeding the path length of the target symlink.