Skip to content

Commit c2ad12f

Browse files
1 parent f7faa36 commit c2ad12f

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-2m67-cxxq-c3h8",
4+
"modified": "2026-03-12T16:37:49Z",
5+
"published": "2026-03-12T16:37:49Z",
6+
"aliases": [
7+
"CVE-2026-32232"
8+
],
9+
"summary": "ZeptoClaw: Path boundary checks bypass via symlink, TOCTOU, and hardlink",
10+
"details": "### Summary\nWorkspace boundary enforcement currently has three related bypass risks. This issue tracks fixing all three in one pull request.\n\n### Details\n\n#### R1 - Dangling Symlink Component Bypass\n- What happens: Path validation can miss dangling symlink components during traversal checks.\n- Why it matters: A symlink that is unresolved at validation time can later resolve to an external location.\n- Impact: Read and write operations may escape workspace boundaries.\n- Affected area: src/security/path.rs (check_symlink_escape).\n\n#### R2 - TOCTOU Between Validation and Use\n- What happens: The path is validated first, then used later for filesystem operations.\n- Why it matters: A concurrent filesystem change can swap path components after validation but before open/write.\n- Impact: Race-based workspace escape is possible.\n- Affected area: Filesystem and file-consuming tools that call validate_path_in_workspace before I/O.\n\n#### R3 - Hardlink Alias Bypass\n- What happens: A file inside workspace can be a hardlink to an inode outside the intended workspace trust boundary.\n- Why it matters: Prefix and symlink checks can pass while data access still mutates or reads external content.\n- Impact: Policy bypass for read/write operations.\n- Affected area: Any tool that reads or writes via validated paths.\n\n#### Risk Matrix\n\n| ID | Risk | Severity | Likelihood | Impact |\n|---|---|---|---|---|\n| R1 | Dangling symlink component bypass | High | Medium | Workspace boundary escape for read/write |\n| R2 | Validate/use TOCTOU race | High | Medium | Race-based boundary escape during file I/O |\n| R3 | Hardlink alias bypass | Medium | Low-Medium | External inode read/write through in-workspace path |\n\n### PoC\n\n#### R1 - Dangling symlink component bypass\n1. Create a symlink inside workspace pointing to a missing target.\n2. Validate a path traversing that symlink.\n3. Create the target directory outside workspace after validation.\n4. Perform file operation and observe potential boundary escape if not fail-closed.\n\n#### R2 - TOCTOU between validation and use\n1. Validate a candidate in-workspace path.\n2. Before open/write, replace an intermediate component with a link to external location.\n3. Continue with the file operation.\n4. Observe boundary escape if operation trusts only stale validation result.\n\n#### R3 - Hardlink alias bypass\n1. Place a hardlink inside workspace that points to an external inode.\n2. Validate the in-workspace hardlink path.\n3. Read or write through this path.\n4. Observe external inode access through a path that appears in-scope.\n\n### Impacts\nUnauthorized cross path boundary\n\n## Credit\n[@zpbrent](https://github.com/zpbrent)\n\n### Patch\n[f50c17e11ae3e2d40c96730abac41974ef2ee2a8](https://github.com/qhkm/zeptoclaw/commit/f50c17e11ae3e2d40c96730abac41974ef2ee2a8)",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:P"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "crates.io",
21+
"name": "zeptoclaw"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.7.6"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.7.5"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/qhkm/zeptoclaw/security/advisories/GHSA-2m67-cxxq-c3h8"
45+
},
46+
{
47+
"type": "WEB",
48+
"url": "https://github.com/qhkm/zeptoclaw/pull/324"
49+
},
50+
{
51+
"type": "WEB",
52+
"url": "https://github.com/qhkm/zeptoclaw/commit/bf004a20d3687a0c1a9e052ec79536e30d6de134"
53+
},
54+
{
55+
"type": "WEB",
56+
"url": "https://github.com/qhkm/zeptoclaw/commit/f50c17e11ae3e2d40c96730abac41974ef2ee2a8"
57+
},
58+
{
59+
"type": "PACKAGE",
60+
"url": "https://github.com/qhkm/zeptoclaw"
61+
},
62+
{
63+
"type": "WEB",
64+
"url": "https://github.com/qhkm/zeptoclaw/releases/tag/v0.7.6"
65+
}
66+
],
67+
"database_specific": {
68+
"cwe_ids": [
69+
"CWE-22",
70+
"CWE-62"
71+
],
72+
"severity": "HIGH",
73+
"github_reviewed": true,
74+
"github_reviewed_at": "2026-03-12T16:37:49Z",
75+
"nvd_published_at": null
76+
}
77+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-46q5-g3j9-wx5c",
4+
"modified": "2026-03-12T16:36:48Z",
5+
"published": "2026-03-12T16:36:48Z",
6+
"aliases": [
7+
"CVE-2026-32231"
8+
],
9+
"summary": "ZeptoClaw: Generic webhook channel trusts caller-supplied identity fields; allowlist is checked against untrusted payload data",
10+
"details": "### Summary\nThe generic webhook channel trusts caller-supplied identity fields (`sender`, `chat_id`) from the request body and applies authorization checks to those untrusted values. Because authentication is optional and defaults to disabled (`auth_token: None`), an attacker who can reach `POST /webhook` can spoof an allowlisted sender and choose arbitrary `chat_id` values, enabling high-risk message spoofing and potential IDOR-style session/chat routing abuse.\n\n### Details\nRelevant code paths:\n\n- `src/channels/webhook.rs:121` sets runtime default `auth_token: None`.\n- `src/config/types.rs:910` also defaults webhook config `auth_token` to `None`.\n- `src/channels/webhook.rs:224` (`validate_auth`) explicitly allows requests when no token is configured.\n- `src/channels/webhook.rs:128` defines `WebhookPayload` with identity fields fully controlled by caller input:\n - `sender: String`\n - `chat_id: String`\n- `src/channels/webhook.rs:421` performs allowlist authorization using `payload.sender`.\n- `src/channels/webhook.rs:433` and `src/channels/webhook.rs:434` create `InboundMessage` using untrusted `payload.sender` and `payload.chat_id`.\n\nWhy this is vulnerable:\n\n- The system treats user-provided JSON identity as authoritative identity.\n- Allowlist enforcement does not verify sender authenticity beyond that payload value.\n- `chat_id` is also attacker-controlled, so routing/session association can be steered to arbitrary chats/conversations.\n- If the webhook is exposed without strong upstream authn/authz controls, spoofing is straightforward.\n\n### PoC\n1. Configure the webhook channel in a vulnerable posture (common default behavior):\n - `enabled = true`\n - `bind_address = \"0.0.0.0\"` (or any reachable interface)\n - `port = 9876`\n - `path = \"/webhook\"`\n - `auth_token = null` (or omitted)\n - `allow_from = [\"trusted-user-1\"]`\n - `deny_by_default = true`\n2. Start ZeptoClaw.\n3. Send a forged request with attacker-chosen `sender` and `chat_id`, without any `Authorization` header:\n\n```bash\ncurl -i -X POST \"http://127.0.0.1:9876/webhook\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\n \"message\":\"FORGED: run privileged workflow\",\n \"sender\":\"trusted-user-1\",\n \"chat_id\":\"victim-chat-42\"\n }'\n```\n\n4. Observe:\n - Response is `HTTP/1.1 200 OK`.\n - Message is accepted as if it originated from `trusted-user-1`.\n - Message is routed under attacker-chosen `chat_id` (`victim-chat-42`).\n\n### Impact\n- Vulnerability type:\n - Authentication/authorization bypass (identity spoofing)\n - IDOR-style routing/control issue via attacker-chosen `chat_id`\n- Affected deployments:\n - Any deployment exposing the generic webhook endpoint without strict upstream authentication and identity binding.\n- Security consequences:\n - Forged inbound messages from spoofed trusted users.\n - Bypass of allowlist intent by injecting allowlisted sender IDs in payload.\n - Cross-chat/session contamination or hijacking by choosing arbitrary `chat_id`.\n - Potential unauthorized downstream agent/tool actions triggered by malicious input.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "crates.io",
21+
"name": "zeptoclaw"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.7.6"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.7.5"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/qhkm/zeptoclaw/security/advisories/GHSA-46q5-g3j9-wx5c"
45+
},
46+
{
47+
"type": "WEB",
48+
"url": "https://github.com/qhkm/zeptoclaw/pull/324"
49+
},
50+
{
51+
"type": "WEB",
52+
"url": "https://github.com/qhkm/zeptoclaw/commit/bf004a20d3687a0c1a9e052ec79536e30d6de134"
53+
},
54+
{
55+
"type": "PACKAGE",
56+
"url": "https://github.com/qhkm/zeptoclaw"
57+
},
58+
{
59+
"type": "WEB",
60+
"url": "https://github.com/qhkm/zeptoclaw/releases/tag/v0.7.6"
61+
}
62+
],
63+
"database_specific": {
64+
"cwe_ids": [
65+
"CWE-306",
66+
"CWE-345"
67+
],
68+
"severity": "HIGH",
69+
"github_reviewed": true,
70+
"github_reviewed_at": "2026-03-12T16:36:48Z",
71+
"nvd_published_at": null
72+
}
73+
}

0 commit comments

Comments
 (0)