Skip to content

[Bug]: Skill download install extracts archives without path traversal checks #9512

@coygeek

Description

@coygeek

Summary

skills.install with a download installer fetches a remote archive and extracts it via system tar/unzip without validating entry paths. The same unsafe extraction pattern also appears in the signal-install command when pulling release assets. A malicious archive containing ../ or absolute paths can write files outside the intended target directory (Zip Slip), leading to arbitrary file overwrite and potential code execution on the host.

Executive Risk Snapshot

  • CVSS v3.1: 7.6 (High)
  • CVSS v4.0: 7.1 (High)
  • Primary risk: skills.install with a download installer fetches a remote archive and extracts it via system tar/unzip without validating entry paths.

Technical Analysis

Describe root cause, what breaks, reproduction details, and fix approach.

Affected Code

File: src/agents/skills-install.ts:202-225 and src/agents/skills-install.ts:291-297

// extractArchive: uses system tar/unzip without validating entry paths
const argv = ["unzip", "-q", archivePath, "-d", targetDir];
return await runCommandWithTimeout(argv, { timeoutMs });

const argv = ["tar", "xf", archivePath, "-C", targetDir];
return await runCommandWithTimeout(argv, { timeoutMs });

// installDownloadSpec: downloads then extracts attacker-controlled archive
const extractResult = await extractArchive({
  archivePath,
  archiveType,
  targetDir,
  stripComponents: spec.stripComponents,
  timeoutMs,
});

File: src/commands/signal-install.ts:150-166

await downloadToFile(assetUrl, archivePath);

if (assetName.endsWith(".zip")) {
  await runCommandWithTimeout(["unzip", "-q", archivePath, "-d", installRoot], {
    timeoutMs: 60_000,
  });
} else if (assetName.endsWith(".tar.gz") || assetName.endsWith(".tgz")) {
  await runCommandWithTimeout(["tar", "-xzf", archivePath, "-C", installRoot], {
    timeoutMs: 60_000,
  });
}

Steps to Reproduce

  1. Create a zip or tar archive containing a file with a path like ../../.ssh/authorized_keys or ../config/openclaw.json.
  2. Publish it at a URL and reference it in a skill download install spec.
  3. Run skills.install for that skill; the archive is extracted and writes outside targetDir.

Recommended Fix

Avoid system tar/unzip for untrusted archives. Use a safe extraction routine that rejects entries whose resolved paths escape targetDir (similar to infra/archive.ts zip handling), or enforce strict path validation before extraction. For tar, use the node tar library with a filter that validates each entry path against targetDir. Apply the same safe extraction logic to the signal-install extraction path as well.

Detailed Risk Analysis

CVSS Assessment

Metric Value
Score 7.6 / 10.0
Severity High
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:H/A:H

CVSS v3.1 Calculator

Attack Surface

How is this reached?

  • Network / [ ] Adjacent / [ ] Local / [ ] Physical

Authentication required?

  • None / [x] Low / [ ] High

Entry point: skills.install with a download installer spec (remote URL) and signal-install command fetching a remote release asset

Exploit Conditions

Complexity: [x] Low / [ ] High
User interaction: [ ] None / [x] Required
Prerequisites: Ability to trigger skill installation (or convince an operator to install a skill) with a malicious download URL, or to supply a malicious release asset to the signal installer.

Impact Assessment

Scope: [x] Unchanged / [ ] Changed

Impact Type Level Description
Confidentiality Low Overwritten files can expose secrets indirectly (e.g., rewriting configs to enable exfil).
Integrity High Arbitrary file overwrite outside the target dir (e.g., shell profiles, config files, scripts).
Availability High Overwriting critical files can break the service or host environment.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions