GHSA-WX3M-WHQV-XV47
Vulnerability from github – Published: 2026-06-05 19:43 – Updated: 2026-06-05 19:46Impact
skillctl 0.1.0 and 0.1.1 contained four path-safety vulnerabilities that, in combination, allowed an attacker to:
-
Exfiltrate arbitrary files on the operator's machine by publishing a malicious skills library containing a symlink inside a skill folder (e.g.
niania → /home/user/.aws/credentials). The symlink fell throughentry.file_type().is_dir()infs_util::copy_dir_all, was dereferenced byfs::copy, and the target's content was copied into the project. A subsequentskillctl pushwould have published the secret to the (possibly public) library — what the reporter called "round-trip path exfiltration". -
Delete arbitrary directories outside the project or library root by crafting a
.skills.tomlwith a maliciousdestinationorsource_pathfield. Both were deserialized asPathBufwith zero validation. BecausePath::joinlets an absolute right-hand side replace the base,destination = "/home/user/.ssh"madecwd.join(...)resolve outside the project;..traversal was equally unguarded. Downstreamremove_dir_allinreplace_folder_contentsthen wiped arbitrary writable directories onskillctl pull/push/detect..skills.tomlis the exact kind of file teams commit and exchange via PR; a single merged malicious PR was sufficient to weaponise the maintainer's nextskillctl pull --all. -
detect --targetaccepted..traversal, even though absolute paths were rejected.--target ../../../etcwould have written outside the library root. -
Fork-name validation accepted
.and..literally, so a fork named..would have produced aPath::joinresolving to the parent directory andfs::renamecould have clobbered it.
Patches
Fixed in v0.1.2:
- Symlinks inside skill folders are hard-rejected at copy time (both top-level source and any descendant entry).
.skills.tomldestinationandsource_pathare validated at load time and reject absolute paths,..components, and Windows-prefix components.- A new
path_safety::safe_joinhelper is wired (defense-in-depth) at every destructive call site inpull.rs/push.rs. detect --targetand the interactive custom-path prompt go through the samevalidate_relative_subpathhelper.validate_fork_nameexplicitly rejects.and...
Threat-model note: the fix is purely lexical (component-level) plus an explicit symlink check at copy time. No filesystem canonicalize calls were added, avoiding TOCTOU windows.
Credit
Reported privately on 2026-05-19 by firebaguette via the Umanio Discord (the reporter declined GitHub credit, so this advisory carries no structured credits field).
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "skillctl"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.1.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-05T19:43:51Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Impact\n\n`skillctl` 0.1.0 and 0.1.1 contained four path-safety vulnerabilities that, in combination, allowed an attacker to:\n\n1. **Exfiltrate arbitrary files on the operator\u0027s machine** by publishing a malicious skills library containing a symlink inside a skill folder (e.g. `niania \u2192 /home/user/.aws/credentials`). The symlink fell through `entry.file_type().is_dir()` in `fs_util::copy_dir_all`, was dereferenced by `fs::copy`, and the target\u0027s content was copied into the project. A subsequent `skillctl push` would have published the secret to the (possibly public) library \u2014 what the reporter called \"round-trip path exfiltration\".\n\n2. **Delete arbitrary directories outside the project or library root** by crafting a `.skills.toml` with a malicious `destination` or `source_path` field. Both were deserialized as `PathBuf` with zero validation. Because `Path::join` lets an absolute right-hand side replace the base, `destination = \"/home/user/.ssh\"` made `cwd.join(...)` resolve outside the project; `..` traversal was equally unguarded. Downstream `remove_dir_all` in `replace_folder_contents` then wiped arbitrary writable directories on `skillctl pull` / `push` / `detect`. `.skills.toml` is the exact kind of file teams commit and exchange via PR; a single merged malicious PR was sufficient to weaponise the maintainer\u0027s next `skillctl pull --all`.\n\n3. **`detect --target` accepted `..` traversal**, even though absolute paths were rejected. `--target ../../../etc` would have written outside the library root.\n\n4. **Fork-name validation accepted `.` and `..` literally**, so a fork named `..` would have produced a `Path::join` resolving to the parent directory and `fs::rename` could have clobbered it.\n\n## Patches\n\nFixed in [v0.1.2](https://github.com/umanio-agency/skillctl/releases/tag/v0.1.2):\n\n- Symlinks inside skill folders are hard-rejected at copy time (both top-level source and any descendant entry).\n- `.skills.toml` `destination` and `source_path` are validated at load time and reject absolute paths, `..` components, and Windows-prefix components.\n- A new `path_safety::safe_join` helper is wired (defense-in-depth) at every destructive call site in `pull.rs` / `push.rs`.\n- `detect --target` and the interactive custom-path prompt go through the same `validate_relative_subpath` helper.\n- `validate_fork_name` explicitly rejects `.` and `..`.\n\nThreat-model note: the fix is purely lexical (component-level) plus an explicit symlink check at copy time. No filesystem `canonicalize` calls were added, avoiding TOCTOU windows.\n\n## Credit\n\nReported privately on 2026-05-19 by **firebaguette** via the Umanio Discord (the reporter declined GitHub credit, so this advisory carries no structured credits field).",
"id": "GHSA-wx3m-whqv-xv47",
"modified": "2026-06-05T19:46:18Z",
"published": "2026-06-05T19:43:51Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/umanio-agency/skillctl/security/advisories/GHSA-wx3m-whqv-xv47"
},
{
"type": "WEB",
"url": "https://github.com/umanio-agency/skillctl/commit/827fff5c0698dd9e48e777d5907cf7bc19b91aca"
},
{
"type": "PACKAGE",
"url": "https://github.com/umanio-agency/skillctl"
},
{
"type": "WEB",
"url": "https://github.com/umanio-agency/skillctl/releases/tag/v0.1.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "skillctl: Path traversal and symlink-follow in skillctl allow arbitrary file disclosure and deletion"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.